The Maybe
container is used when a series of computations
could return None
at any point.
Maybe
consist of two types: Some
and Nothing
.
We have a convenient method to create different Maybe
types
based on just a single value:
>>> from returns.maybe import Maybe
>>> str(Maybe.new(1))
'<Some: 1>'
>>> str(Maybe.new(None))
'<Nothing>'
It might be very useful for complex operations like the following one:
>>> from attr import dataclass
>>> from typing import Optional
>>> from returns.maybe import Maybe
>>> @dataclass
... class Address(object):
... street: Optional[str]
>>> @dataclass
... class User(object):
... address: Optional[Address]
>>> @dataclass
... class Order(object):
... user: Optional[User]
>>> def get_street_adderess(order: Order) -> Maybe[str]:
... return Maybe.new(order.user).map(
... lambda user: user.address,
... ).map(
... lambda address: address.street,
... )
...
>>> with_address = Order(User(Address('Some street')))
>>> empty_user = Order(None)
>>> empty_address = Order(User(None))
>>> empty_street = Order(User(Address(None)))
>>> str(get_street_adderess(with_address)) # all fields are not None
'<Some: Some street>'
>>> str(get_street_adderess(empty_user))
'<Nothing>'
>>> str(get_street_adderess(empty_address))
'<Nothing>'
>>> str(get_street_adderess(empty_street))
'<Nothing>'
One may ask: “How is that different to the Optional[]
type?”
That’s a really good question!
Consider the same code to get the street name
without Maybe
and using raw Optional
values:
order: Order # some existing Order instance
street: Optional[str] = None
if order.user is not None:
if order.user.address is not None:
street = order.user.address.street
It looks way uglier and can grow even more uglier and complex when new logic will be introduced.
Typing will only work correctly if decorator_plugin is used. This happens due to mypy issue.
Sometimes we have to deal with functions
that dears to return Optional
values!
We have to work with it the carefully
and write if x is not None:
everywhere.
Luckily, we have your back! maybe
function decorates
any other function that returns Optional
and converts it to return Maybe
instead:
>>> from typing import Optional
>>> from returns.maybe import Maybe, maybe
>>> @maybe
... def number(num: int) -> Optional[int]:
... if num > 0:
... return num
... return None
>>> result: Maybe[int] = number(1)
>>> str(result)
'<Some: 1>'
When working with regular Python,
you might need regular Optional[a]
values.
You can easily get one from your Maybe
container at any point in time:
>>> from returns.maybe import Maybe
>>> assert Maybe.new(1).value_or(None) == 1
>>> assert Maybe.new(None).value_or(None) is None
As you can see, revealed type of .value_or(None)
is Optional[a]
.
Use it a fallback.
We do have IOResult
, but we don’t have IOMaybe
. Why?
Because when dealing with IO
there are a lot of possible errors.
And Maybe
represents just None
and the value.
It is not useful for IO
related tasks.
So, use Result
instead, which can represent what happened to your IO
.
You can convert Maybe
to Result
and back again with special Converters.
Maybe
(inner_value)[source]¶Bases: returns.primitives.container.BaseContainer
, typing.Generic
Represents a result of a series of commutation that can return None
.
An alternative to using exceptions or constant is None
checks.
Maybe
is an abstract type and should not be instantiated directly.
Instead use Some
and Nothing
.
success_type
¶alias of _Some
failure_type
¶alias of _Nothing
new
(inner_value)[source]¶Creates new instance of Maybe
container based on a value.
>>> from returns.maybe import Maybe, Some, Nothing
>>> assert Maybe.new(1) == Some(1)
>>> assert Maybe.new(None) == Nothing
Maybe
[+_ValueType]
map
(function)[source]¶Composes successful container with a pure function.
>>> from returns.maybe import Some, Nothing
>>> def mappable(string: str) -> str:
... return string + 'b'
...
>>> assert Some('a').map(mappable) == Some('ab')
>>> assert Nothing.map(mappable) == Nothing
Maybe
[~_NewValueType]
bind
(function)[source]¶Composes successful container with a function that returns a container.
>>> from returns.maybe import Nothing, Maybe, Some
>>> def bindable(string: str) -> Maybe[str]:
... return Some(string + 'b')
...
>>> assert Some('a').bind(bindable) == Some('ab')
>>> assert Nothing.bind(bindable) == Nothing
Maybe
[~_NewValueType]
value_or
(default_value)[source]¶Get value from succesful container or default value from failed one.
>>> from returns.maybe import Nothing, Some
>>> assert Some(0).value_or(1) == 0
>>> assert Nothing.value_or(1) == 1
Union
[+_ValueType, ~_NewValueType]
unwrap
()[source]¶Get value from successful container or raise exception for failed one.
>>> from returns.maybe import Nothing, Some
>>> assert Some(1).unwrap() == 1
>>> Nothing.unwrap()
Traceback (most recent call last):
...
returns.primitives.exceptions.UnwrapFailedError
+_ValueType
failure
()[source]¶Get failed value from failed container or raise exception from success.
>>> from returns.maybe import Nothing, Some
>>> assert Nothing.failure() is None
>>> Some(1).failure()
Traceback (most recent call last):
...
returns.primitives.exceptions.UnwrapFailedError
None
lift
(function)[source]¶Lifts function to be wrapped in Maybe
for better composition.
In other words, it modifies the function’s
signature from: a -> b
to: Maybe[a] -> Maybe[b]
Works similar to map()
, but has inverse semantics.
This is how it should be used:
>>> from returns.maybe import Maybe, Nothing, Some
>>> def example(argument: int) -> float:
... return argument / 2
...
>>> assert Maybe.lift(example)(Some(2)) == Some(1.0)
>>> assert Maybe.lift(example)(Nothing) == Nothing
See also
Some
(inner_value)[source]¶Public unit function of protected _Some
type.
>>> from returns.maybe import Some, Nothing
>>> str(Some(1))
'<Some: 1>'
>>> assert Some(None) == Nothing
Maybe
[+_ValueType]
Nothing
= <returns.maybe._Nothing object>¶Public unit value of protected _Nothing
type.
maybe
(function)[source]¶Decorator to covert None
returning function to Maybe
container.
Supports both async and regular functions.
>>> from typing import Optional
>>> from returns.maybe import Nothing, Some, maybe
>>> @maybe
... def might_be_none(arg: int) -> Optional[int]:
... if arg == 0:
... return None
... return 1 / arg
...
>>> assert might_be_none(0) == Nothing
>>> assert might_be_none(1) == Some(1.0)