python/returns
Result & ResultE
wefree
2022. 9. 4. 21:37
https://returns.readthedocs.io/en/latest/pages/result.html
- scala Either 와 비슷한듯
- error 를 Exception 으로 고정해 scala Try 처럼 사용해도 좋을 듯, ResultE[T] = Result[T, Exception]
- Result.do 와 pipe 도 알아두자 (flow 대신에 Result.do 를 사용하자?)
from typing import Callable
from returns.pipeline import flow, pipe
from returns.pointfree import bind
from returns.result import Result, Success, Failure, ResultE
x: ResultE[int] = Success(2)
# x: Result[int, Exception] = Success(2)
y: ResultE[int] = Success(3)
# y: Result[int, Exception] = Success(3)
def sum_of(x: int, y: int) -> int:
return x + y
# scala 에서의 for-comprehension ?
z = Result.do(
a + b # sum_of(a, b) 로도 표현 가능
for a in x
for b in y
) # Success(5)
#############################################################
def f(x: int) -> ResultE[int]:
return Success(x + 1)
def g(x: int) -> ResultE[int]:
return Success(x * 2)
# 시작 값으로 computation (이것 보다는 아래의 Result.do 를 쓰는 것이 좋아 보임)
flow(
1, # instance
f, # function
bind(g) # function
) # Success(4)
# 위의 flow 를 Result.do 로 표현
Result.do(
y
for x in f(1)
for y in g(x)
) # Success(4)
# function composition: 시작 값이 없이 function composition
h: Callable[[int], ResultE[int]] = pipe(
f,
bind(g)
)
h(1) # Success(4)
#############################################################
def recover(e: Exception) -> int:
return 123
def recover_with(e: Exception) -> ResultE[int]:
return Success(456)
def find_user(id: int) -> ResultE[int]:
if id == 1:
return Success(id + 100)
else:
return Failure(ValueError("invalid value"))
find_user(1).alt(recover) # 101
find_user(2).alt(recover) # 123
find_user(1).lash(recover_with) # 101
find_user(2).lash(recover_with) # 456
Result + Maybe
from returns.maybe import Maybe, Some
from returns.pipeline import is_successful
from returns.result import Success, ResultE, Failure, Result
# maybe: Maybe[int] = Some(3)
maybe: Maybe[int] = Maybe.empty
# result: ResultE[int] = Success(1)
result: ResultE[int] = Failure(ValueError)
z: Maybe[int] | ResultE[int] = Result.do(
x + y
for x in maybe
for y in result
)
# 방법 1
v: int = z.value_or(9)
print(v)
# 방법 2
if is_successful(z):
v = z.unwrap()
print(v)
else:
print('Error')
# 방법 3
match z:
case Some(v) | Success(v):
print(v)
case Maybe.empty:
print('Maybe Error')
case Failure(e):
print('Result Error')
Result v.s Maybe
from returns.converters import maybe_to_result, result_to_maybe
from returns.maybe import Maybe, Some
from returns.pipeline import is_successful
from returns.result import Result, Success, ResultE
x: ResultE[int] = Success(2)
a: Maybe[int] = result_to_maybe(x)
b: Maybe[int] = Some(3)
y: Result[int, None] = maybe_to_result(b)
c: Maybe[int] = Nothing
z: ResultE[int] = maybe_to_result(c, Exception('error')) # Failure("error")
z.failure() # Get failed value or raise exception.
is_successful(b)
is_successful(y)
@safe
from returns.result import Success, Failure, safe
# Will convert type to: Callable[[int], ResultE[float]]
@safe
def divide(n: int, m: int) -> float:
return n / m
match divide(1, 0):
case Success(10):
print('Result is "10"')
case Success(value):
print(f'result={value}')
case Failure(ZeroDivisionError()):
print('"ZeroDivisionError" was raised')
case Failure(_):
print('The division was a failure')
safe 직접 호출
from returns.result import ResultE, safe
def divide(n: int, m: int) -> float:
return n / m
z: ResultE[float] = safe(divide)(1, 0)
print(z) # <Failure: division by zero>