Result
is obviously a result of some series of computations.
It might succeed with some resulting value.
Or it might return an error with some extra details.
Result
consist of two types: Success
and Failure
.
Success
represents successful operation result
and Failure
indicates that something has failed.
from returns.result import Result, Success, Failure
def find_user(user_id: int) -> Result['User', str]:
user = User.objects.filter(id=user_id)
if user.exists():
return Success(user[0])
return Failure('User was not found')
user_search_result = find_user(1)
# => Success(User{id: 1, ...})
user_search_result = find_user(0) # id 0 does not exist!
# => Failure('User was not found')
When is it useful?
When you do not want to use exceptions to break your execution scope.
Or when you do not want to use None
to represent empty values,
since it will raise TypeError
somewhere
and other None
exception-friends.
Make sure to check out how to compose container with pipe and @pipeline! Read more about them if you want to compose your containers easily.
safe
is used to convert
regular functions that can throw exceptions to functions
that return Result
type.
Supports both async and regular functions.
from returns.result import safe
@safe # Will convert type to: Callable[[int], Result[float, Exception]]
def divide(number: int) -> float:
return number / number
divide(1)
# => Success(1.0)
divide(0)
# => Failure(ZeroDivisionError)
Typing will only work correctly if decorator_plugin is used. This happens due to mypy issue.
Use Success
or Failure
together with the explicit annotation.
Python’s type system does not allow us to do much, so this is required:
def callback(arg: int) -> Result[float, int]:
return Success(float(arg))
first: Result[int, int] = Success(1)
first.bind(callback)
Otherwise it would raise a mypy
error:
first = Success(1)
first.bind(callback)
# Argument 1 to "bind" of "Result" has incompatible type
# "Callable[[int], Result[float, int]]";
# expected "Callable[[int], Result[float, NoReturn]]"
This happens because mypy
is unable to implicitly
cast NoReturn
to any other type.
Success
and _Success
?¶You might wonder why Success
is a function
and _Success
is internal type, that should not be used directly.
Well, that’s a complicated question. Let’s find out.
Let’s begin with haskell
definition:
Prelude> :t Left 1
Left 1 :: Num a => Either a b
Prelude> :t Right 1
Right 1 :: Num b => Either a b
As you can see: Left
(Failure
) and Right
(Success
)
are type constructors: that return Either a b
(Result[b, a]
) value.
It means, that there’s no single type Left a
that makes
sense without Right b
. Only their duality makes sence to us.
In python
we have functions that can be used as type constructors.
That’s why we use Success
and Failure
functions.
But, when we need to implement
the behaviour of these types - we use real classes inside.
That’s how we know what to do in each particular case.
In haskell
we use pattern matching for this.
That’s why we have public
type constructor functions: Success
and Failure
and internal implementation.
Result
(inner_value)[source]¶Bases: typing.Generic
, returns.primitives.container.BaseContainer
Base class for _Failure
and _Success
.
Result
does not have
map
(function)[source]¶Abstract method to compose container with a pure function.
Result
[~_NewValueType, +_ErrorType]
bind
(function)[source]¶Abstract method to compose a container with another container.
Result
[~_NewValueType, +_ErrorType]
fix
(function)[source]¶Abstract method to compose failed container and a pure function.
This pure function should return a new state for a successful container.
Result
[~_NewValueType, +_ErrorType]
alt
(function)[source]¶Abstract method to compose failed container and a pure function.
This pure function should return a new state for a new failed container.
Result
[+_ValueType, ~_NewErrorType]
rescue
(function)[source]¶Abstract method to compose a failed container with another container.
This method is the oposite of .bind()
.
Result
[+_ValueType, ~_NewErrorType]
Success
(inner_value)[source]¶Public unit function of protected _Success type.
Result
[+_ValueType]
Failure
(inner_value)[source]¶Public unit function of protected _Failure type.
Result
[+_ErrorType]
safe
(function)[source]¶Decorator to covert exception throwing function to ‘Result’ container.
Should be used with care, since it only catches ‘Exception’ subclasses. It does not catch ‘BaseException’ subclasses.
Supports both async and regular functions.
>>> @safe
... def might_raise(arg: int) -> float:
... return 1 / arg
...
>>> might_raise(1) == Success(1.0)
True
>>> isinstance(might_raise(0), _Failure)
True