Make your functions return something meaningful, typed, and safe!
Brings functional programming to Python land
Provides a bunch of primitives to write declarative business logic
Enforces better architecture
Fully typed with annotations and checked with mypy
, PEP561 compatible
Adds emulated Higher Kinded Types support
Provides type-safe interfaces to create your own data-types with enforced laws
Has a bunch of helpers for better composition
Pythonic and pleasant to write and to read 🐍
Support functions and coroutines, framework agnostic
Easy to start: has lots of docs, tests, and tutorials
Quickstart right now!
pip install returns
You are also required to configure
mypy
correctly and install our plugin
to fix this existing issue:
# In setup.cfg or mypy.ini:
[mypy]
plugins =
returns.contrib.mypy.returns_plugin
We also recommend to use the same mypy
settings we use.
Make sure you know how to get started, check out our docs! Try our demo.
Maybe container that allows you to write None
-free code
RequiresContext container that allows you to use typed functional dependency injection
Result container that let’s you to get rid of exceptions
IO container and IOResult that marks all impure operations and structures them
Future container and FutureResult to work with async
code
Write your own container! You would still have all the features for your own types (including full existing code reuse and type-safety)
None
is called the worst mistake in the history of Computer Science.
So, what can we do to check for None
in our programs?
You can use builtin Optional type
and write a lot of if some is not None:
conditions.
But, having ``null`` checks here and there makes your code unreadable.
user: Optional[User]
discount_program: Optional['DiscountProgram'] = None
if user is not None:
balance = user.get_balance()
if balance is not None:
credit = balance.credit_amount()
if credit is not None and credit > 0:
discount_program = choose_discount(credit)
Or you can use
Maybe container!
It consists of Some
and Nothing
types,
representing existing state and empty (instead of None
) state respectively.
from typing import Optional
from returns.maybe import Maybe, maybe
@maybe # decorator to convert existing Optional[int] to Maybe[int]
def bad_function() -> Optional[int]:
...
maybe_number: Maybe[float] = bad_function().bind_optional(
lambda number: number / 2,
)
# => Maybe will return Some[float] only if there's a non-None value
# Otherwise, will return Nothing
You can be sure that .bind_optional()
method won’t be called for Nothing
.
Forget about None
-related errors forever!
We can also bind a Optional
-returning function over a container.
To achieve this, we are going to use .bind_optional
method.
And here’s how your initial refactored code will look:
user: Optional[User]
# Type hint here is optional, it only helps the reader here:
discount_program: Maybe['DiscountProgram'] = Maybe.from_optional(
user,
).bind_optional( # This won't be called if `user is None`
lambda real_user: real_user.get_balance(),
).bind_optional( # This won't be called if `real_user.get_balance()` is None
lambda balance: balance.credit_amount(),
).bind_optional( # And so on!
lambda credit: choose_discount(credit) if credit > 0 else None,
)
Much better, isn’t it?
Many developers do use some kind of dependency injection in Python. And usually it is based on the idea that there’s some kind of a container and assembly process.
Functional approach is much simpler!
Imagine that you have a django
based game, where you award users with points for each guessed letter in a word (unguessed letters are marked as '.'
):
from django.http import HttpRequest, HttpResponse
from words_app.logic import calculate_points
def view(request: HttpRequest) -> HttpResponse:
user_word: str = request.POST['word'] # just an example
points = calculate_points(user_word)
... # later you show the result to user somehow
# Somewhere in your `words_app/logic.py`:
def calculate_points(word: str) -> int:
guessed_letters_count = len([letter for letter in word if letter != '.'])
return _award_points_for_letters(guessed_letters_count)
def _award_points_for_letters(guessed: int) -> int:
return 0 if guessed < 5 else guessed # minimum 6 points possible!
Awesome! It works, users are happy, your logic is pure and awesome. But, later you decide to make the game more fun: let’s make the minimal accountable letters threshold configurable for an extra challenge.
You can just do it directly:
def _award_points_for_letters(guessed: int, threshold: int) -> int:
return 0 if guessed < threshold else guessed
The problem is that _award_points_for_letters
is deeply nested.
And then you have to pass threshold
through the whole callstack,
including calculate_points
and all other functions that might be on the way.
All of them will have to accept threshold
as a parameter!
This is not useful at all!
Large code bases will struggle a lot from this change.
Ok, you can directly use django.settings
(or similar)
in your _award_points_for_letters
function.
And ruin your pure logic with framework specific details. That’s ugly!
Or you can use RequiresContext
container. Let’s see how our code changes:
from django.conf import settings
from django.http import HttpRequest, HttpResponse
from words_app.logic import calculate_points
def view(request: HttpRequest) -> HttpResponse:
user_word: str = request.POST['word'] # just an example
points = calculate_points(user_words)(settings) # passing the dependencies
... # later you show the result to user somehow
# Somewhere in your `words_app/logic.py`:
from typing_extensions import Protocol
from returns.context import RequiresContext
class _Deps(Protocol): # we rely on abstractions, not direct values or types
WORD_THRESHOLD: int
def calculate_points(word: str) -> RequiresContext[int, _Deps]:
guessed_letters_count = len([letter for letter in word if letter != '.'])
return _award_points_for_letters(guessed_letters_count)
def _award_points_for_letters(guessed: int) -> RequiresContext[int, _Deps]:
return RequiresContext(
lambda deps: 0 if guessed < deps.WORD_THRESHOLD else guessed,
)
And now you can pass your dependencies in a really direct and explicit way.
And have the type-safety to check what you pass to cover your back.
Check out RequiresContext docs for more. There you will learn how to make '.'
also configurable.
We also have RequiresContextResult for context-related operations that might fail. And also RequiresContextIOResult and RequiresContextFutureResult.
Please, make sure that you are also aware of Railway Oriented Programming.
Consider this code that you can find in any python
project.
import requests
def fetch_user_profile(user_id: int) -> 'UserProfile':
"""Fetches UserProfile dict from foreign API."""
response = requests.get('/api/users/{0}'.format(user_id))
response.raise_for_status()
return response.json()
Seems legit, does it not?
It also seems like a pretty straightforward code to test.
All you need is to mock requests.get
to return the structure you need.
But, there are hidden problems in this tiny code sample that are almost impossible to spot at the first glance.
import requests
from returns.result import Result, safe
from returns.pipeline import flow
from returns.pointfree import bind
def fetch_user_profile(user_id: int) -> Result['UserProfile', Exception]:
"""Fetches `UserProfile` TypedDict from foreign API."""
return flow(
user_id,
_make_request,
bind(_parse_json),
)
@safe
def _make_request(user_id: int) -> requests.Response:
# TODO: we are not yet done with this example, read more about `IO`:
response = requests.get('/api/users/{0}'.format(user_id))
response.raise_for_status()
return response
@safe
def _parse_json(response: requests.Response) -> 'UserProfile':
return response.json()
Now we have a clean and a safe and declarative way to express our business needs:
We start from making a request, that might fail at any moment,
Then parsing the response if the request was successful,
And then return the result.
Now, instead of returning regular values we return values wrapped inside a special container thanks to the @safe decorator. It will return Success[YourType] or Failure[Exception]. And will never throw exception at us!
We also use flow and bind functions for handy and declarative composition.
This way we can be sure that our code won’t break in random places due to some implicit exception. Now we control all parts and are prepared for the explicit errors.
We are not yet done with this example, let’s continue to improve it in the next chapter.
Let’s look at our example from another angle. All its functions look like regular ones: it is impossible to tell whether they are pure or impure from the first sight.
It leads to a very important consequence: we start to mix pure and impure code together. We should not do that!
When these two concepts are mixed we suffer really bad when testing or reusing it. Almost everything should be pure by default. And we should explicitly mark impure parts of the program.
That’s why we have created IO
container
to mark impure functions that never fail.
These impure functions use random
, current datetime, environment, or console:
import random
import datetime as dt
from returns.io import IO
def get_random_number() -> IO[int]: # or use `@impure` decorator
return IO(random.randint(1, 10)) # isn't pure, because random
now: Callable[[], IO[dt.datetime]] = impure(dt.datetime.now)
@impure
def return_and_show_next_number(previous: int) -> int:
next_number = previous + 1
print(next_number) # isn't pure, because does IO
return next_number
Now we can clearly see which functions are pure and which ones are impure. This helps us a lot in building large applications, unit testing you code, and composing business logic together.
As it was already said, we use IO
when we handle functions that do not fail.
What if our function can fail and is impure?
Like requests.get()
we had earlier in our example.
Then we have to use a special IOResult
type instead of a regular Result
.
Let’s find the difference:
Our _parse_json
function always returns
the same result (hopefully) for the same input:
you can either parse valid json
or fail on invalid one.
That’s why we return pure Result
, there’s no IO
inside
Our _make_request
function is impure and can fail.
Try to send two similar requests with and without internet connection.
The result will be different for the same input.
That’s why we must use IOResult
here: it can fail and has IO
So, in order to fulfill our requirement and separate pure code from impure one, we have to refactor our example.
Let’s make our IO explicit!
import requests
from returns.io import IOResult, impure_safe
from returns.result import safe
from returns.pipeline import flow
from returns.pointfree import bind_result
def fetch_user_profile(user_id: int) -> IOResult['UserProfile', Exception]:
"""Fetches `UserProfile` TypedDict from foreign API."""
return flow(
user_id,
_make_request,
# before: def (Response) -> UserProfile
# after safe: def (Response) -> ResultE[UserProfile]
# after bind_result: def (IOResultE[Response]) -> IOResultE[UserProfile]
bind_result(_parse_json),
)
@impure_safe
def _make_request(user_id: int) -> requests.Response:
response = requests.get('/api/users/{0}'.format(user_id))
response.raise_for_status()
return response
@safe
def _parse_json(response: requests.Response) -> 'UserProfile':
return response.json()
And later we can use unsafe_perform_io somewhere at the top level of our program to get the pure (or “real”) value.
As a result of this refactoring session, we know everything about our code:
Which parts can fail,
Which parts are impure,
How to compose them in a smart, readable, and typesafe manner.
There are several issues with async
code in Python:
You cannot call async
function from a sync one
Any unexpectedly thrown exception can ruin your whole event loop
Ugly composition with lots of await
statements
Future
and FutureResult
containers solve these issues!
The main feature of Future is that it allows to run async code while maintaining sync context. Let’s see an example.
Let’s say we have two functions,
the first
one returns a number and the second
one increments it:
async def first() -> int:
return 1
def second(): # How can we call `first()` from here?
return first() + 1 # Boom! Don't do this. We illustrate a problem here.
If we try to just run first()
, we will just create an unawaited coroutine.
It won’t return the value we want.
But, if we would try to run await first()
,
then we would need to change second
to be async
.
And sometimes it is not possible for various reasons.
However, with Future
we can “pretend” to call async code from sync code:
from returns.future import Future
def second() -> Future[int]:
return Future(first()).map(lambda num: num + 1)
Without touching our first
async function
or making second
async we have achieved our goal.
Now, our async value is incremented inside a sync function.
However, Future
still requires to be executed inside a proper eventloop:
import anyio # or asyncio, or any other lib
# We can then pass our `Future` to any library: asyncio, trio, curio.
# And use any event loop: regular, uvloop, even a custom one, etc
assert anyio.run(second().awaitable) == 2
As you can see Future
allows you
to work with async functions from a sync context.
And to mix these two realms together.
Use raw Future
for operations that cannot fail or raise exceptions.
Pretty much the same logic we had with our IO
container.
We have already covered how ``Result` <#result-container>`_ works for both pure and impure code. The main idea is: we don’t raise exceptions, we return them. It is especially critical in async code, because a single exception can ruin all our coroutines running in a single eventloop.
We have a handy combination of Future
and Result
containers: FutureResult
.
Again, this is exactly like IOResult
, but for impure async code.
Use it when your Future
might have problems:
like HTTP requests or filesystem operations.
You can easily turn any wild throwing coroutine into a calm FutureResult
:
import anyio
from returns.future import future_safe
from returns.io import IOFailure
@future_safe
async def raising():
raise ValueError('Not so fast!')
ioresult = anyio.run(raising.awaitable) # all `Future`s return IO containers
assert ioresult == IOFailure(ValueError('Not so fast!')) # True
Using FutureResult
will keep your code safe from exceptions.
You can always await
or execute inside an eventloop any FutureResult
to get sync IOResult
instance to work with it in a sync manner.
Previously, you had to do quite a lot of await
ing while writing async
code:
async def fetch_user(user_id: int) -> 'User':
...
async def get_user_permissions(user: 'User') -> 'Permissions':
...
async def ensure_allowed(permissions: 'Permissions') -> bool:
...
async def main(user_id: int) -> bool:
# Also, don't forget to handle all possible errors with `try / except`!
user = await fetch_user(user_id) # We will await each time we use a coro!
permissions = await get_user_permissions(user)
return await ensure_allowed(permissions)
Some people are ok with it, but some people don’t like this imperative style. The problem is that there was no choice.
But now, you can do the same thing in functional style!
With the help of Future
and FutureResult
containers:
import anyio
from returns.future import FutureResultE, future_safe
from returns.io import IOSuccess, IOFailure
@future_safe
async def fetch_user(user_id: int) -> 'User':
...
@future_safe
async def get_user_permissions(user: 'User') -> 'Permissions':
...
@future_safe
async def ensure_allowed(permissions: 'Permissions') -> bool:
...
def main(user_id: int) -> FutureResultE[bool]:
# We can now turn `main` into a sync function, it does not `await` at all.
# We also don't care about exceptions anymore, they are already handled.
return fetch_user(user_id).bind(get_user_permissions).bind(ensure_allowed)
correct_user_id: int # has required permissions
banned_user_id: int # does not have required permissions
wrong_user_id: int # does not exist
# We can have correct business results:
assert anyio.run(main(correct_user_id).awaitable) == IOSuccess(True)
assert anyio.run(main(banned_user_id).awaitable) == IOSuccess(False)
# Or we can have errors along the way:
assert anyio.run(main(wrong_user_id).awaitable) == IOFailure(
UserDoesNotExistError(...),
)
Or even something really fancy:
from returns.pointfree import bind
from returns.pipeline import flow
def main(user_id: int) -> FutureResultE[bool]:
return flow(
fetch_user(user_id),
bind(get_user_permissions),
bind(ensure_allowed),
)
Later we can also refactor our logical functions to be sync
and to return FutureResult
.
Lovely, isn’t it?
Want more? Go to the docs! Or read these articles:
Do you have an article to submit? Feel free to open a pull request!
Starting is really fast!
You can integrate returns
into any project at any stage.
You can use it fully or partially. With or without types.
returns
is a very flexible library!
You can even just start using it without any deep theory around this project. But, you can always address our learning materials which will unravel all parts of functional programming with useful examples and simple terms.
One of the most frequent questions Python developers ask: why would we need this?
Basically, the answer is that returns
provides
useful abstractions that solve some problems every developer has:
Maybe
helps to work with None
in a type-safe way
Result
helps
to work with exceptions in a type-safe way
IO
helps to separate pure code
from impure code to make your architecture better
Future
helps to write await
free code
RequiresContext
helps
to inject dependencies in a very readable, explicit, type-safe, and clean way
Pipelines can be used independently or together with the types above to create complex, declarative, and type-safe data pipelines
On top of that we provide useful interfaces that allows you to switch implementation on the fly. For example, you can write code that works the same way for sync and async execution flows. While being fully type-safe at the same time.
And you can write your own primitives that will solve any other problem you can possible have based on our existing or your custom interfaces.
In other words, returns
unlocks insane powers
of typed-functional programming to a regular Python developer.
returns
is a pure Python library. Install it as usual:
pip install returns # or better use poetry
This step is optional.
If you use mypy
for type-checking, than you will need to configure it.
We really recommend using mypy
with this project though.
Because we have put a lot of efforts into the typing part.
Check out our docs on mypy.
We also have built-in integrations with pytest and hypothesis. Also, there is developer tooling you might enjoy.
Do you want to learn new awesome concepts? Then, start reading our “Userguide”!
It has everything you need! Reading order matters.
However, this is optional.
You can still use returns
without a deep-dive into theory.
You can extend returns
and build your own stuff!
Particularly, you can add new interfaces, new containers, and new integrations.
See this guide.
Join our chat to get help or advice.
Container is a concept that allows you to write code around the existing wrapped values while maintaining the execution context.
List of supported containers:
Maybe
to handle None
cases
Result
to handle possible exceptions
IO
to mark explicit IO
actions
Future
to work with async
code
RequiresContext
to pass context to your functions (DI and similar)
There are also some combintations like
IOResult
,
FutureResult
,
RequiresContextResult
,
RequiresContextIOResult
and
RequiresContextFutureResult
.
We will show you container’s simple API of one attribute and several simple methods.
The main idea behind a container is that it wraps some internal state.
That’s what
._inner_value
is used for.
And we have several functions to create new containers based on the previous state. And we can see how this state is evolving during the execution.
State evolution.¶
We use two methods to create a new container from the previous one.
bind
and map
.
The difference is simple:
map
works with functions that return regular value
bind
works with functions that return new container of the same type
We have returns.interfaces.mappable.MappableN.map()
to compose containers with regular functions.
Here’s how it looks:
Illustration of map
method.¶
>>> from typing import Any
>>> from returns.result import Success, Result
>>> def double(state: int) -> int:
... return state * 2
>>> result: Result[int, Any] = Success(1).map(double)
>>> assert str(result) == '<Success: 2>'
>>> result: Result[int, Any] = result.map(lambda state: state + 1)
>>> assert str(result) == '<Success: 3>'
The same works with built-in functions as well:
>>> from returns.io import IO
>>> io = IO('bytes').map(list)
>>> str(io)
"<IO: ['b', 'y', 't', 'e', 's']>"
The second method is bind
. It is a bit different.
We pass a function that returns another container to it.
returns.interfaces.bindable.BindableN.bind()
is used to literally bind two different containers together.
Here’s how it looks:
Illustration of bind
method.¶
from returns.result import Result, Success
def may_fail(user_id: int) -> Result[float, str]:
...
value: Result[int, str] = Success(1)
# Can be assumed as either Success[float] or Failure[str]:
result: Result[float, str] = value.bind(may_fail)
Note
All containers support these methods.
Because all containers implement
returns.interfaces.mappable.MappableN
and
returns.interfaces.bindable.BindableN
.
You can read more about methods that some other containers support and interfaces behind them.
All returns.interfaces.applicative.ApplicativeN
containers
support special .from_value
method
to construct a new container from a raw value.
>>> from returns.result import Result
>>> assert str(Result.from_value(1)) == '<Success: 1>'
There are also other methods in other interfaces. For example, here are some of them:
returns.interfaces.specific.maybe.MaybeLikeN.from_optional()
creates a value from Optional
value
>>> from returns.maybe import Maybe, Some, Nothing
>>> assert Maybe.from_optional(1) == Some(1)
>>> assert Maybe.from_optional(None) == Nothing
returns.interfaces.failable.DiverseFailableN.from_failure()
creates a failing container from a value
>>> from returns.result import Result, Failure
>>> assert Result.from_failure(1) == Failure(1)
There are many other constructors! Check out concrete types and their interfaces.
We have already seen how we can work with one container and functions that receive a single argument.
Let’s say you have a function of two arguments and two containers:
>>> def sum_two_numbers(first: int, second: int) -> int:
... return first + second
And here are our two containers:
>>> from returns.io import IO
>>> one = IO(1)
>>> two = IO(2)
The naive approach to compose two IO
containers and a function
would be too hard to show here.
Luckily, we support partial application and the .apply()
method.
Here are the required steps:
We make sum_two_numbers
to receive partial arguments
We create a new container that wraps sum_two_numbers
function as a value
We then call .apply()
twice to pass each value
It can be done like so:
>>> from returns.curry import curry
>>> from returns.io import IO
>>> @curry
... def sum_two_numbers(first: int, second: int) -> int:
... return first + second
>>> one = IO(1)
>>> two = IO(2)
>>> assert two.apply(one.apply(IO(sum_two_numbers))) == IO(3)
But, there are other ways to make sum_two_numbers
partial.
One can use partial
as well:
>>> from returns.curry import partial
>>> one = IO(1)
>>> two = IO(2)
>>> assert two.apply(one.apply(
... IO(lambda x: partial(sum_two_numbers, x)),
... )) == IO(3)
Or even native lambda
functions:
>>> one = IO(1)
>>> two = IO(2)
>>> assert two.apply(one.apply(
... IO(lambda x: lambda y: sum_two_numbers(x, y)),
... )) == IO(3)
It would be faster, but not as elegant (and type-safe).
Imagine that you have two take 10 random numbers and then sum they to get the final result.
So, here’s how your code will look like:
>>> import random
>>> from returns.io import IO
>>> def random_number() -> IO[int]:
... return IO(2) # Example, basically alias of ``random.randint(1, 5)``
>>> numbers = [random_number() for _ in range(10)]
>>> assert len(numbers) == 10
>>> assert all(isinstance(number, IO) for number in numbers)
So, how to sum these random values into a single IO[int]
value?
That’s where
Fold.loop
really helps!
>>> from typing import Callable
>>> from returns.iterables import Fold
>>> def sum_two_numbers(first: int) -> Callable[[int], int]:
... return lambda second: first + second
>>> assert Fold.loop(
... numbers, # let's loop on our ``IO`` values
... IO(0), # starting from ``0`` value
... sum_two_numbers, # and getting the sum of each two numbers in a loop
... ) == IO(20)
We can also change the initial element to some other value:
>>> assert Fold.loop(
... numbers,
... IO(5), # now we will start from ``5``, not ``0`
... sum_two_numbers,
... ) == IO(25)
Fold.loop
is eager. It will be executed for all items in your iterable.
You might end up with an iterable of containers:
>>> from typing import List
>>> from returns.maybe import Maybe, Some, Nothing, maybe
>>> source = {'a': 1, 'b': 2}
>>> fetched_values: List[Maybe[int]] = [
... maybe(source.get)(key)
... for key in ('a', 'b')
... ]
To work with iterable of containers,
it is recommended to cast it into a container with the iterable inside
using the Fold.collect
method:
>>> from returns.iterables import Fold
>>> assert Fold.collect(fetched_values, Some(())) == Some((1, 2))
Any falsy values will result in a falsy result (pun intended):
>>> fetched_values: List[Maybe[int]] = [
... maybe(source.get)(key)
... for key in ('a', 'c') # 'c' is missing!
... ]
>>> assert Fold.collect(fetched_values, Some(())) == Nothing
You can also use a different strategy to fetch values you need,
to do just that we have
Fold.collect_all
method:
>>> fetched_values: Maybe[int] = [
... maybe(source.get)(key)
... for key in ('a', 'c') # 'c' is missing!
... ]
>>> assert Fold.collect_all(fetched_values, Some(())) == Some((1,))
We support any Iterable[T]
input type
and return a Container[Sequence[T]]
.
You can subclass Fold
type to change how any of these methods work.
We like to think of returns
as immutable structures.
You cannot mutate the inner state of the created container,
because we redefine __setattr__
and __delattr__
magic methods.
You cannot also set new attributes to container instances,
since we are using __slots__
for better performance and strictness.
Well, nothing is really immutable in python, but you were warned.
We also provide returns.primitives.types.Immutable
mixin
that users can use to quickly make their classes immutable.
We try to make our containers optionally type safe.
What does it mean?
It is still good old python
, do whatever you want without mypy
If you are using mypy
you will be notified about type violations
We also ship PEP561
compatible .pyi
files together with the source code.
In this case these types will be available to users
when they install our application.
We also ship custom mypy
plugins to overcome some existing problems,
please make sure to use them,
since they increase your developer experience and type-safety level:
Check out our docs on using our mypy plugins.
BaseContainer
is a base class for all other containers.
It defines some basic things like representation, hashing, pickling, etc.
Bases: Immutable
Utility class to provide all needed magic methods to the context.
Wraps the given value in the Container.
‘value’ is any arbitrary value of any type including functions.
Used to compare two ‘Container’ objects.
other (Any
) –
bool
Loading state from pickled data.
state (Union
[_PickleState
, Any
]) –
None
Containers can serve many different purposes
(while still serving the main one: composition)
for example, some of them
(Result
and Maybe
) are used
to work with different types of errors
starting with NullPointerException
to arbitrary user-defined ones.
When talking about error handling we use a concept of Railway oriented programming. It means that flow of our program has two tracks:
Successful one: where everything goes perfectly: HTTP requests work, database is always serving us data, parsing values does not fail
Failed one: where something went wrong
We can switch from track to track: we can fail something or we can fix the situation.
Railway oriented programming.¶
We also support two special methods to work with “failed” values:
returns.interfaces.altable.AltableN.alt()
transforms error to another error
that works only when container is in failed state,
is the opposite of returns.interfaces.mappable.MappableN.map()
method
returns.interfaces.lashable.LashableN.lash()
is the opposite of returns.interfaces.bindable.BindableN.bind()
method
that works only when container is in failed state
Let’s start from the first one:
alt
method allows to change your error type.
Illustration of alt
method.¶
>>> from returns.result import Failure
>>> assert Failure(1).alt(str) == Failure('1')
The second method is lash
. It is a bit different.
We pass a function that returns another container to it.
returns.interfaces.lashable.LashableN.lash()
is used to literally bind two different containers together.
It can also lash your flow and get on the successful track again:
Illustration of lash
method.¶
>>> from returns.result import Result, Failure, Success
>>> def tolerate_exception(state: Exception) -> Result[int, Exception]:
... if isinstance(state, ZeroDivisionError):
... return Success(0)
... return Failure(state)
>>> value: Result[int, Exception] = Failure(ZeroDivisionError())
>>> result: Result[int, Exception] = value.lash(tolerate_exception)
>>> assert result == Success(0)
>>> value2: Result[int, Exception] = Failure(ValueError())
>>> result2: Result[int, Exception] = value2.lash(tolerate_exception)
>>> # => Failure(ValueError())
From typing perspective .alt
and .lash
are exactly the same as .map
and .bind
but only work with the second type argument instead of the first one:
from returns.result import Result
first: Result[int, int]
second: Result[int, int]
reveal_type(first.map(str))
# => Result[str, int]
reveal_type(second.alt(str))
# => Result[int, str]
Note
Not all containers support these methods,
only containers that implement
returns.interfaces.lashable.LashableN
and
returns.interfaces.altable.AltableN
For example, IO
based containers
and RequiresContext
cannot be alted or lashed.
And we have two more functions to unwrap inner state of containers into a regular types:
.unwrap
returns a value if it is possible,
raises returns.primitives.exceptions.UnwrapFailedError
otherwise
>>> from returns.result import Failure, Success
>>> from returns.maybe import Some, Nothing
>>> assert Success(1).value_or(None) == 1
>>> assert Some(0).unwrap() == 0
>>> Failure(1).unwrap()
Traceback (most recent call last):
...
returns.primitives.exceptions.UnwrapFailedError
>>> Nothing.unwrap()
Traceback (most recent call last):
...
returns.primitives.exceptions.UnwrapFailedError
For failing containers you can
use returns.interfaces.unwrappable.Unwrapable.failure()
to unwrap the failed state:
>>> assert Failure(1).failure() == 1
>>> Success(1).failure()
Traceback (most recent call last):
...
returns.primitives.exceptions.UnwrapFailedError
Be careful, since this method will raise an exception
when you try to .failure()
a successful container.
Note
Not all containers support these methods,
only containers that implement
returns.interfaces.unwrappable.Unwrappable
.
For example, IO
based containers
and RequiresContext
cannot be unwrapped.
Note
Some containers also have .value_or()
helper method.
Example:
>>> from returns.result import Success, Failure
>>> assert Success(1).value_or(None) == 1
>>> assert Failure(1).value_or(None) is None
Higher Kinded Types is a new concept for Python developers. But, it is totally not new in general!
So, let’s start with the detailed explanation: what Higher Kinded Types are?
We can start with the very basic example.
Let’s say we have a function that transforms type A
into a type B
.
These types A
and B
can be some specific ones, for example:
>>> def from_a_to_b(arg: int) -> str:
... return str(arg)
>>> assert from_a_to_b(1) == '1'
That’s what we already know and use. Let’s scale things up!
The next thing we can do with types is to write generic types.
What are generic types?
Basically, they are some types that contain other types inside.
Like List[int]
is a list of integers: [1, 2, 3]
.
We know that List[int]
has a shape of a list
and contents of int
.
We can also write functions that work with generics:
>>> from typing import List
>>> def all_to_str(arg: List[int]) -> List[str]:
... return [str(item) for item in arg]
>>> assert all_to_str([1, 2]) == ['1', '2']
There’s one more thing about generics we want to notice at this point. Different generics do have different numbers of type arguments:
List
has a single type argument:
List[Value]
or Maybe[Value]
Dict
has two type arguments:
Dict[Key, Value]
or Result[Value, Error]
Generator
has three type arguments: Generator[Yield, Send, Return]
or RequiresContextResult[Value, Error, Env]
That’s what we call a kind.
So, List
and Maybe
have a kind of 1
,
Dict
and Result
have kind of 2
,
Generator
and RequiresContextResult
have a kind of 3
.
So, let’s go one level further.
Let’s say you have a function that copies all values of a passed argument. We can define this function as:
>>> from typing import TypeVar
>>> ValueType = TypeVar('ValueType')
>>> def copy(arg: ValueType) -> ValueType:
... ...
This function can work with any type. It receives something and then returns the same value back. That’s the whole point of copying!
But, there are different functions, that do different things with types.
For example, we can write a function that converts
a value inside any Container1
(a base class for all our containers)
from int
to str
:
We can also write functions that work with generics:
>>> from returns.interfaces.container import Container1
>>> def to_str(container: Container1[int]) -> Container1[str]:
... return container.map(str)
And here’s how it can be used:
>>> from returns.maybe import Maybe
>>> from returns.io import IO
>>> assert to_str(Maybe.from_value(1)) == Maybe.from_value('1')
>>> assert to_str(IO.from_value(1)) == IO.from_value('1')
It works just fine! But! It has a very important thing inside.
All calls to to_str
will return Container1
type,
not something specific:
reveal_type(to_str(Maybe.from_value(1))) # Container1[str]
reveal_type(to_str(IO.from_value(1))) # Container1[str]
But, we know that this is not true.
When we pass a Maybe
in - we get the Maybe
back.
When we pass a IO
in - we get the IO
back.
How can we fix this problem? With @overload
!
>>> from typing import overload
>>> from returns.maybe import Maybe
>>> from returns.io import IO
>>> @overload
... def to_str(arg: Maybe[int]) -> Maybe[str]:
... ...
>>> @overload
... def to_str(arg: IO[int]) -> IO[str]:
... ...
We kinda fixed it! Now, our calls will reveal the correct types for these three examples:
reveal_type(to_str(Maybe.from_value(1))) # Maybe[str]
reveal_type(to_str(IO.from_value(1))) # IO[str]
But, there’s an important limitation with this solution: no other types are allowed in this function anymore. So, you will try to use it with any other type, it won’t be possible.
To overcome current @overload
decorators limitations,
we can imagine a syntax like this:
from typing import TypeVar
from returns.interfaces.container import Container1
T = TypeVar('T', bound=Container1)
def all_to_str(arg: T[int]) -> T[str]:
...
Sadly, this does not work. Because TypeVar
cannot be used with []
.
We have to find some other way.
So, that’s where returns
saves the day!
Note
Technical note: this feature requires mypy plugin.
The main idea is that we can rewrite T[int]
as Kind1[T, int]
.
Let’s see how it works:
>>> from returns.primitives.hkt import Kind1
>>> from returns.interfaces.container import ContainerN
>>> from typing import TypeVar
>>> T = TypeVar('T', bound=ContainerN)
>>> def to_str(container: Kind1[T, int]) -> Kind1[T, str]:
... return container.map(str)
Now, this will work almost correctly!
Why almost? Because the revealed type will be Kind1
.
reveal_type(to_str(Maybe.from_value(1))) # Kind1[Maybe, str]
reveal_type(to_str(IO.from_value(1))) # Kind1[IO, str]
That’s not something we want. We don’t need Kind1
,
we need real Maybe
or IO
values.
The final solution is to decorate to_str
with @kinded
:
>>> from returns.primitives.hkt import kinded
>>> @kinded
... def to_str(container: Kind1[T, int]) -> Kind1[T, str]:
... return container.map(str)
Now, it will be fully working:
reveal_type(to_str(Maybe.from_value(1))) # Maybe[str]
reveal_type(to_str(IO.from_value(1))) # IO[str]
And the thing about this approach is that it will be:
Fully type-safe. It works with correct interface ContainerN
,
returns the correct type, has correct type transformation
Is opened for further extension and even custom types
As it was said Maybe[int]
, Result[str, int]
,
and RequiresContextResult[str, int, bool]
are different
in terms of a number of type arguments.
We support different kinds:
Kind1[Maybe, int]
is similar to Maybe[int]
Kind2[Result, str, int]
is similar to Result[str, int]
Kind3[RequiresContextResult, str, int, bool]
is similar to RequiresContextResult[str, int, bool]
You can use any of them freely.
Later you will learn how to create your own types that support kinds!
The first position in all KindN
types
can be occupied by either Instance
type or TypeVar
with bound=
.
Let’s see an example:
>>> from typing import TypeVar
>>> from returns.primitives.hkt import KindN, kinded
>>> from returns.interfaces.mappable import MappableN
>>> _FirstType = TypeVar('_FirstType')
>>> _SecondType = TypeVar('_SecondType')
>>> _ThirdType = TypeVar('_ThirdType')
>>> _MappableKind = TypeVar('_MappableKind', bound=MappableN)
>>> @kinded
... def works_with_interface(
... container: KindN[_MappableKind, _FirstType, _SecondType, _ThirdType],
... ) -> KindN[_MappableKind, str, _SecondType, _ThirdType]:
... return container.map(str)
This version of works_with_interface
will work
with any subtype of MappableN
.
Because we use _MappableKind
in its definition.
And _MappableKind
is a TypeVar
bound to MappableN
.
Arguments of non MappableN
subtypes will be rejected by a type-checker:
>>> from returns.maybe import Maybe
>>> from returns.io import IO
>>> from returns.result import Success
>>> assert works_with_interface(Maybe.from_value(1)) == Maybe.from_value('1')
>>> assert works_with_interface(IO.from_value(1)) == IO.from_value('1')
>>> assert works_with_interface(Success(1)) == Success('1')
In contrast, we can work directly with some specific type,
let’s say Maybe
container:
>>> from returns.maybe import Maybe
>>> @kinded
... def works_with_maybe(
... container: KindN[Maybe, _FirstType, _SecondType, _ThirdType],
... ) -> KindN[Maybe, str, _SecondType, _ThirdType]:
... return container.map(str)
>>> assert works_with_maybe(Maybe.from_value(1)) == Maybe.from_value('1')
Function works_with_maybe
will work correctly with Maybe
instance.
Other types will be rejected.
So, choose wisely which mechanism you need.
Bases: Generic
[_InstanceType
, _TypeArgType1
, _TypeArgType2
, _TypeArgType3
]
Emulation support for Higher Kinded Types.
Consider KindN
to be an alias of Generic
type.
But with some extra goodies.
KindN
is the top-most type for other Kind
types
like Kind1
, Kind2
, Kind3
, etc.
The only difference between them is how many type arguments they can hold.
Kind1
can hold just two type arguments: Kind1[IO, int]
which is almost equals to IO[int]
.
Kind2
can hold just two type arguments: Kind2[IOResult, int, str]
which is almost equals to IOResult[int, str]
.
And so on.
The idea behind KindN
is that one cannot write this code:
from typing import TypeVar
T = TypeVar('T')
V = TypeVar('V')
def impossible(generic: T, value: V) -> T[V]:
return generic(value)
But, with KindN
this becomes possible in a form of Kind1[T, V]
.
Note
To make sure it works correctly,
your type has to be a subtype of KindN
.
We use a custom mypy
plugin to make sure types are correct.
Otherwise, it is currently impossible to properly type this.
We use “emulated Higher Kinded Types” concept. Read the whitepaper: https://bit.ly/2ABACx2
KindN
does not exist in runtime. It is used just for typing.
There are (and must be) no instances of this type directly.
Implementation details
We didn’t use ABCMeta
to disallow its creation,
because we don’t want to have
a possible metaclass conflict with other metaclasses.
Current API allows you to mix KindN
anywhere.
We allow _InstanceType
of KindN
to be Instance
type or TypeVarType
with bound=...
.
Type alias for kinds with one type argument.
alias of KindN
[_InstanceType
, _TypeArgType1
, Any
, Any
]
Type alias for kinds with two type arguments.
alias of KindN
[_InstanceType
, _TypeArgType1
, _TypeArgType2
, Any
]
Type alias for kinds with three type arguments.
alias of KindN
[_InstanceType
, _TypeArgType1
, _TypeArgType2
, _TypeArgType3
]
Bases: KindN
[_InstanceType
, _TypeArgType1
, _TypeArgType2
, _TypeArgType3
]
Base class for your containers.
Notice, that we use KindN
/ Kind1
to annotate values,
but we use SupportsKindN
/ SupportsKind1
to inherit from.
Implementation details
The only thing this class does is: making sure that the resulting classes
won’t have __getattr__
available during the typechecking phase.
Needless to say, that __getattr__
during runtime - never exists at all.
Type alias used for inheritance with one type argument.
alias of SupportsKindN
[_InstanceType
, _TypeArgType1
, NoReturn
, NoReturn
]
Type alias used for inheritance with two type arguments.
alias of SupportsKindN
[_InstanceType
, _TypeArgType1
, _TypeArgType2
, NoReturn
]
Type alias used for inheritance with three type arguments.
alias of SupportsKindN
[_InstanceType
, _TypeArgType1
, _TypeArgType2
, _TypeArgType3
]
Turns Kind1[IO, int]
type into real IO[int]
type.
Should be used when you are left with accidental KindN
instance
when you really want to have the real type.
Works with type arguments of any length.
We use a custom mypy
plugin to make sure types are correct.
Otherwise, it is currently impossible to properly type this.
In runtime it just returns the passed argument, nothing really happens:
>>> from returns.io import IO
>>> from returns.primitives.hkt import Kind1
>>> container: Kind1[IO, int] = IO(1)
>>> assert dekind(container) is container
However, please, do not use this function unless you know exactly what you are doing and why do you need it.
kind (KindN
[TypeVar
(_InstanceType
, covariant=True), TypeVar
(_TypeArgType1
, covariant=True), TypeVar
(_TypeArgType2
, covariant=True), TypeVar
(_TypeArgType3
, covariant=True)]) –
TypeVar
(_InstanceType
, covariant=True)
Bases: Protocol
[_FunctionDefType
]
Protocol that tracks kinded functions calls.
We use a custom mypy
plugin to make sure types are correct.
Otherwise, it is currently impossible to properly type this.
Decorator to be used when you want to dekind the function’s return type.
Does nothing in runtime, just returns its argument.
We use a custom mypy
plugin to make sure types are correct.
Otherwise, it is currently impossible to properly type this.
Here’s an example of how it should be used:
>>> from typing import TypeVar
>>> from returns.primitives.hkt import KindN, kinded
>>> from returns.interfaces.bindable import BindableN
>>> _Binds = TypeVar('_Binds', bound=BindableN) # just an example
>>> _Type1 = TypeVar('_Type1')
>>> _Type2 = TypeVar('_Type2')
>>> _Type3 = TypeVar('_Type3')
>>> @kinded
... def bindable_identity(
... container: KindN[_Binds, _Type1, _Type2, _Type3],
... ) -> KindN[_Binds, _Type1, _Type2, _Type3]:
... return container # just do nothing
As you can see, here we annotate our return type as
-> KindN[_Binds, _Type1, _Type2, _Type3]
,
it would be true without @kinded
decorator.
But, @kinded
decorator dekinds the return type and infers
the real type behind it:
>>> from returns.io import IO, IOResult
>>> assert bindable_identity(IO(1)) == IO(1)
>>> # => Revealed type: 'IO[int]'
>>> iores: IOResult[int, str] = IOResult.from_value(1)
>>> assert bindable_identity(iores) == iores
>>> # => Revealed type: 'IOResult[int, str]'
The difference is very clear in methods
modules, like:
Raw returns.methods.bind.internal_bind()
that returns KindN
instance
User-facing returns.methods.bind.bind()
that returns the container type
You must use this decorator for your own kinded functions as well.
function (TypeVar
(_FunctionType
, bound= Callable
)) –
Kinded
[TypeVar
(_FunctionType
, bound= Callable
)]
We provide a lot of generic interfaces to write our bundled and your own custom types.
These interfaces are designed:
To be subclassed
To provide abstract methods to implement in your own types
To enforce correctness on final types
To attach critical laws to be checked
We use Higher Kinded Types to define abstract methods.
Reading about interfaces will be the most useful if you plan to create your own containers.
All the non-specific interfaces (e.g. MappableN, BindableN, ApplicativeN) can have Nth types, at the maximum of three possible types. What does this mean?
MappableN
interface,
for example, can have one, two or three possible types. See the example below:
>>> from typing import NoReturn
>>> from returns.interfaces.mappable import (
... MappableN, Mappable1, Mappable2, Mappable3,
... )
>>> one_type: MappableN[int, NoReturn, NoReturn]
>>> two_types: MappableN[int, str, NoReturn]
>>> three_types: MappableN[int, str, bool]
>>> # We have a shortcut for each amount of arguments to reduce the boilerplate
>>> one_type: Mappable1[int]
>>> two_types: Mappable2[int, str]
>>> three_type: Mappable3[int, str, bool]
Note
Useful links before you start here:
We follow a very specific naming convention in our interface names.
If interface does not depend on the number of types
it works with and is always the same, we name it as is.
For example, Equable
is always the same
and does not depend on the number of type arguments.
We use adjectives to name these interfaces.
Secondly, if interface depends on the number of type arguments,
it is named with N
suffix in the end.
It would always have numeric aliases for each number of arguments supported.
For example, MappableN
, Mappable1
, Mappable2
, and Mappable3
.
The last criteria we have to decided on naming is
“whether this interface always the same or it can have slight variations”?
That’s why we have ResultLikeN
and ResultBasedN
interfaces.
Because ResultBasedN
has two extra methods compared to ResultLikeN
.
We use Like
suffix for interfaces that describes some similar types.
We use Based
suffix for interfaces that descire almost concrete types.
Some interfaces define its laws as values. These laws can be viewed as tests that are attached to the specific interface.
We are able to check them of any type that implements a given interfaces with laws by our own check_all_laws hypothesis plugin.
In this docs we are going to describe each general interface and its laws.
Something is considered mappable if we can map
it using a function,
generally map
is a method that accepts a function.
An example in this library is Maybe
,
that implements the Mappable
interface:
>>> from returns.maybe import Maybe, Some
>>> def can_be_mapped(string: str) -> str:
... return string + '!'
>>> maybe_str: Maybe[str] = Some('example')
>>> assert maybe_str.map(can_be_mapped) == Some('example!')
MappableN
interface helps us to
create our own mappable container like Maybe
.
>>> from typing import Callable, TypeVar
>>> from returns.interfaces.mappable import Mappable1
>>> from returns.primitives.hkt import SupportsKind1
>>> from returns.primitives.container import BaseContainer
>>> _NumberType = TypeVar('_NumberType')
>>> _NewNumberType = TypeVar('_NewNumberType')
>>> class Number(
... BaseContainer,
... SupportsKind1['Number', _NumberType],
... Mappable1[_NumberType],
... ):
... def __init__(self, inner_value: _NumberType) -> None:
... super().__init__(inner_value)
...
... def map( # This method is required by Mappable
... self,
... function: Callable[[_NumberType], _NewNumberType]
... ) -> 'Number[_NewNumberType]':
... return Number(function(self._inner_value))
With our Number
mappable class we can compose easily math functions with it.
>>> def my_math_function(number: int) -> int:
... return number - 1
>>> number: Number[int] = Number(-41)
>>> assert number.map(my_math_function).map(abs) == Number(42)
To make sure your Mappable
implementation is right,
you can apply the Mappable
laws on it to test.
Identity Law
:
When we pass the identity function to the map
method,
the Mappable
instance has to be the same, unchanged.
>>> from returns.functions import identity
>>> mappable_number: Number[int] = Number(1)
>>> assert mappable_number.map(identity) == Number(1)
Associative Law
:
Given two functions, x
and y
,
calling the map method with x
function and after that calling
with y
function must have the
same result if we compose them together.
>>> from returns.functions import compose
>>> def add_one(number: int) -> int:
... return number + 1
>>> def multiply_by_ten(number: int) -> int:
... return number * 10
>>> mappable_number: Number[int] = Number(9)
>>> assert mappable_number.map(
... add_one,
... ).map(
... multiply_by_ten,
... ) == mappable_number.map(
... compose(add_one, multiply_by_ten),
... )
Bindable is something that we can bind with a function. Like
Maybe
, so
BindableN
interface will help
us to create our custom bindable.
>>> from typing import Callable, TypeVar
>>> from returns.interfaces.bindable import Bindable1
>>> from returns.primitives.hkt import SupportsKind1, Kind1, dekind
>>> from returns.primitives.container import BaseContainer
>>> _NumberType = TypeVar('_NumberType')
>>> _NewNumberType = TypeVar('_NewNumberType')
>>> class Number(
... BaseContainer,
... SupportsKind1['Number', _NumberType],
... Bindable1[_NumberType],
... ):
... def __init__(self, inner_value: _NumberType) -> None:
... super().__init__(inner_value)
...
... def bind( # This method is required by Bindable
... self,
... function: Kind1[
... 'Number',
... Callable[[_NumberType], 'Number[_NewNumberType]'],
... ],
... ) -> 'Number[_NewNumberType]':
... return dekind(function(self._inner_value))
And here’s how we can use it:
>>> def double(arg: int) -> Number[int]:
... return Number(arg * 2)
>>> number = Number(5)
>>> assert number.bind(double) == Number(10)
Something is considered applicative
if it is a functor already and,
moreover, we can apply
another container to it
and construct a new value with .from_value
method.
An example in this library is Maybe
,
that implements the Mappable
and Applicative
interfaces:
>>> from returns.maybe import Maybe, Some
>>> maybe_str = Maybe.from_value('example')
>>> maybe_func = Maybe.from_value(len) # we use function as a value!
>>> assert maybe_str.apply(maybe_func) == Some(7)
As you see, apply
takes a container with a function inside
and applies it to the current value inside the container.
This way we really execute Maybe.from_value(len('example'))
.
ApplicativeN
which is a subtype of
MappableN
interface helps us to
create our own applicative container like Maybe
.
>>> from typing import Callable, TypeVar
>>> from returns.interfaces.applicative import Applicative1
>>> from returns.primitives.hkt import SupportsKind1, Kind1, dekind
>>> from returns.primitives.container import BaseContainer
>>> _NumberType = TypeVar('_NumberType')
>>> _NewNumberType = TypeVar('_NewNumberType')
>>> class Number(
... BaseContainer,
... SupportsKind1['Number', _NumberType],
... Applicative1[_NumberType],
... ):
... def __init__(self, inner_value: _NumberType) -> None:
... super().__init__(inner_value)
...
... def map( # This method is required by Mappable
... self,
... function: Callable[[_NumberType], _NewNumberType]
... ) -> 'Number[_NewNumberType]':
... return Number(function(self._inner_value))
...
... def apply( # This method is required by Applicative
... self,
... container: Kind1[
... 'Number',
... Callable[[_NumberType], _NewNumberType],
... ],
... ) -> 'Number[_NewNumberType]':
... return Number.from_value(
... dekind(container._inner_value(self._inner_value)),
... )
...
... @classmethod
... def from_value( # This method is required by Applicative
... cls,
... inner_value: _NewNumberType,
... ) -> 'Number[_NewNumberType]':
... return Number(inner_value)
With our Number
mappable class we can compose easily math functions with it.
>>> def my_math_function(number: int) -> int:
... return number - 1
>>> number = Number(3)
>>> number_function = Number.from_value(my_math_function)
>>> assert number.apply(number_function) == Number(2)
To make sure your Applicative
implementation is right,
you can apply the Applicative
laws on it to test.
Identity Law
:
When we pass an applicative instance
with wrapped identity function to the apply
method,
the Applicative
has to be the same, unchanged.
>>> from returns.functions import identity
>>> applicative_number: Number[int] = Number(1)
>>> assert applicative_number.apply(
... applicative_number.from_value(identity),
... ) == Number(1)
Interchange Law
:
We can start our composition with both raw value and a function.
>>> def function(arg: int) -> int:
... return arg + 1
>>> raw_value = 5
>>> assert Number.from_value(raw_value).apply(
... Number.from_value(function),
... ) == Number.from_value(function).apply(
... Number.from_value(lambda inner: inner(raw_value)),
... )
Homomorphism Law
:
The homomorphism law says that
applying a wrapped function to a wrapped value is the same
as applying the function to the value in the normal way
and then using .from_value
on the result.
>>> def function(arg: int) -> int:
... return arg + 1
>>> raw_value = 5
>>> assert Number.from_value(
... function(raw_value),
... ) == Number.from_value(raw_value).apply(
... Number.from_value(function),
... )
Composition Law
:
Applying two functions twice is the same
as applying their composition once.
>>> from returns.functions import compose
>>> def first(arg: int) -> int:
... return arg * 2
>>> def second(arg: int) -> int:
... return arg + 1
>>> instance = Number(5)
>>> assert instance.apply(
... Number.from_value(compose(first, second)),
... ) == instance.apply(
... Number.from_value(first),
... ).apply(
... Number.from_value(second),
... )
Plus all laws from MappableN
interface.
ContainerN
is a central piece of our library.
It is an interface that combines
ApplicativeN
and
BindableN
together.
So, in other words: Container
is an Apllicative
that you can bind
!
>>> from typing import Callable, TypeVar
>>> from returns.interfaces.container import Container1
>>> from returns.primitives.hkt import SupportsKind1, Kind1, dekind
>>> from returns.primitives.container import BaseContainer
>>> _NumberType = TypeVar('_NumberType')
>>> _NewNumberType = TypeVar('_NewNumberType')
>>> class Number(
... BaseContainer,
... SupportsKind1['Number', _NumberType],
... Container1[_NumberType],
... ):
... def __init__(self, inner_value: _NumberType) -> None:
... super().__init__(inner_value)
...
... def map( # This method is required by Mappable
... self,
... function: Callable[[_NumberType], _NewNumberType]
... ) -> 'Number[_NewNumberType]':
... return Number(function(self._inner_value))
...
... def bind( # This method is required by Bindable
... self,
... function: Kind1[
... 'Number',
... Callable[[_NumberType], 'Number[_NewNumberType]'],
... ],
... ) -> 'Number[_NewNumberType]':
... return dekind(function(self._inner_value))
...
... def apply( # This method is required by Applicative
... self,
... container: Kind1[
... 'Number',
... Callable[[_NumberType], _NewNumberType],
... ],
... ) -> 'Number[_NewNumberType]':
... return Number.from_value(
... container._inner_value(self._inner_value),
... )
...
... @classmethod
... def from_value( # This method is required by Applicative
... cls,
... inner_value: _NewNumberType,
... ) -> 'Number[_NewNumberType]':
... return Number(inner_value)
This code gives us an opportunity to use Number
with map
, apply
, and bind
as we already did in the examples above.
To make sure other people will be able to use your implementation, it should respect three new laws.
Left Identity
:
If we bind
a function to our bindable must have to be
the same result as passing the value directly to the function.
>>> def can_be_bound(value: int) -> Number[int]:
... return Number(value)
>>> assert Number.from_value(5).bind(can_be_bound) == can_be_bound(5)
Right Identity
:
If we pass the bindable constructor through bind
must
have to be the same result as instantiating the bindable on our own.
>>> number = Number(2)
>>> assert number.bind(Number) == Number(2)
Associative Law
:
Given two functions, x
and y
, calling the bind
method with x
function and after that calling with y
function
must have the same result if we bind with
a function that passes the value to x
and then bind the result with y
.
>>> def minus_one(arg: int) -> Number[int]:
... return Number(arg - 1)
>>> def half(arg: int) -> Number[int]:
... return Number(arg // 2)
>>> number = Number(9)
>>> assert number.bind(minus_one).bind(half) == number.bind(
... lambda value: minus_one(value).bind(half),
... )
Plus all laws from MappableN
and ApplicativeN
interfaces.
We have way more interfaces with different features! We have covered all of them in the technical docs.
So, use them to enforce type-safety of your own containers.
We also have a whole package of different specific interfaces
that will help you to create containers based on our internal types,
like Result
.
We have .interfaces.*
types that can be applied to any possible type.
There’s nothing they know about other types or returns
package.
We also have a special .interfaces.specific
package
where we have types that know about other types in returns
.
For example, MappableN
from .interfaces
only knows about .map
method. It does not require anything else.
But, ResultLikeN
from .interfaces.specific.result
does require to have .bind_result
method
which relies on our Result
type.
That’s the only difference. Build your own types with any of those interfaces.
Some types like
ResultLikeN
do not have type aliases for one type argument in a form of ResultLike1
.
Why does Mappable1
exists and ResultLike1
does not?
Because Mappable1
does make sense.
But, ResultLike1
requires at least two (value and error) types to exist.
The same applies for ReaderLike1
and ReaderResultLike1
and ReaderResultLike2
.
We don’t support type aliases for types that won’t make sense.
MappableN
and BindableN
?¶While MappableN
you have to pass a pure function, like:
>>> def can_be_mapped(string: str) -> str:
... return string
with Bindable we have to pass a function that returns another container:
>>> from returns.maybe import Maybe
>>> def can_be_bound(string: str) -> Maybe[str]:
... return Some(string + '!')
The main difference is the return type.
The consequence of this is big!
BindableN
allows to change the container type.
While MappableN
cannot do that.
So, Some.bind(function)
can be evaluated to both Some
and Nothing
.
While Some.map(function)
will always stay as Some
.
ResultLikeN
is just an intention of having a result
(e.g. FutureResult
),
it’s not the result yet. While ResultBasedN
is a concrete result
(e.g. IOResult
),
it has the desired result value.
Because of this difference between them is why we can’t unwrap a ResultLikeN
container, it does not have the real result yet.
See the example below using FutureResult
to get a IOResult
:
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.interfaces.specific.future_result import FutureResultBasedN
>>> from returns.interfaces.specific.ioresult import (
... IOResultBasedN,
... IOResultLikeN,
... )
>>> from returns.interfaces.specific.result import ResultLikeN, ResultBasedN
>>> from returns.io import IOSuccess, IOResult
>>> from returns.result import Success, Result
>>> async def coro(arg: int) -> Result[int, str]:
... return Success(arg + 1)
>>> # `result_like` does not have the result we want (Result[int, str])
>>> # it's just the intention of having one,
>>> # we have to await it to get the real result
>>> result_like: FutureResult[int, str] = FutureResult(coro(1))
>>> assert isinstance(result_like, FutureResultBasedN)
>>> assert isinstance(result_like, IOResultLikeN)
>>> assert isinstance(result_like, ResultLikeN)
>>> # `anyio.run(...)` will await our coroutine and give the real result to us
>>> result: IOResult[int, str] = anyio.run(result_like.awaitable)
>>> assert isinstance(result, IOResultBasedN)
>>> assert isinstance(result, ResultLikeN)
>>> # Compare it with the real result:
>>> assert isinstance(Success(1), ResultBasedN)
Note
The same difference applies to all *ResultLikeN
vs *ResultBasedN
(e.g. IOResultLikeN
and IOResultBasedN
)
Here’s a full overview of all our interfaces:
Let’s review it one by one.
Bases: LawSpecDef
Equality laws.
Description: https://bit.ly/34D40iT
Value should be equal to itself.
first (TypeVar
(_EqualType
, bound= Equable)) –
None
Interface for types that can be compared with real values.
Not all types can, because some don’t have the value at a time:
- Future
has to be awaited to get the value
- Reader
has to be called to get the value
Bases: LawSpecDef
Mappable or functor laws.
https://en.wikibooks.org/wiki/Haskell/The_Functor_class#The_functor_laws
Mapping identity over a value must return the value unchanged.
mappable (MappableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
None
Mapping twice or mapping a composition is the same thing.
mappable (MappableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
first (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_NewType1
)]) –
second (Callable
[[TypeVar
(_NewType1
)], TypeVar
(_NewType2
)]) –
None
Bases: Generic
[_FirstType
, _SecondType
, _ThirdType
], Lawful
[MappableN[_FirstType, _SecondType, _ThirdType]
]
Allows to chain wrapped values in containers with regular functions.
Behaves like a functor.
ClassVar
[Sequence
[Law
]] = (<returns.primitives.laws.Law1 object>, <returns.primitives.laws.Law3 object>)¶Some classes and interfaces might have laws, some might not have any.
Allows to run a pure function over a container.
self (TypeVar
(_MappableType
, bound= MappableN)) –
function (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_UpdatedType
)]) –
KindN
[TypeVar
(_MappableType
, bound= MappableN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Type alias for kinds with one type argument.
alias of MappableN
[_FirstType
, NoReturn
, NoReturn
]
Bases: Generic
[_FirstType
, _SecondType
, _ThirdType
]
Represents a “context” in which calculations can be executed.
Bindable
allows you to bind together
a series of calculations while maintaining
the context of that specific container.
In contrast to returns.interfaces.lashable.LashableN
,
works with the first type argument.
Applies ‘function’ to the result of a previous calculation.
And returns a new container.
self (TypeVar
(_BindableType
, bound= BindableN)) –
function (Callable
[[TypeVar
(_FirstType
)], KindN
[TypeVar
(_BindableType
, bound= BindableN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
KindN
[TypeVar
(_BindableType
, bound= BindableN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Type alias for kinds with one type argument.
alias of BindableN
[_FirstType
, NoReturn
, NoReturn
]
Bases: LawSpecDef
Applicative mappable laws.
Definition: https://bit.ly/3hC8F8E Discussion: https://bit.ly/3jffz3L
Identity law.
If we apply wrapped identity
function to a container,
nothing happens.
container (ApplicativeN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
None
Interchange law.
Basically we check that we can start our composition
with both raw_value
and function
.
Great explanation: https://stackoverflow.com/q/27285918/4842742
raw_value (TypeVar
(_FirstType
)) –
container (ApplicativeN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_NewType1
)]) –
None
Homomorphism law.
The homomorphism law says that
applying a wrapped function to a wrapped value is the same
as applying the function to the value in the normal way
and then using .from_value
on the result.
raw_value (TypeVar
(_FirstType
)) –
container (ApplicativeN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_NewType1
)]) –
None
Composition law.
Applying two functions twice is the same as applying their composition once.
container (ApplicativeN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
first (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_NewType1
)]) –
second (Callable
[[TypeVar
(_NewType1
)], TypeVar
(_NewType2
)]) –
None
Bases: MappableN
[_FirstType
, _SecondType
, _ThirdType
], Lawful
[ApplicativeN[_FirstType, _SecondType, _ThirdType]
]
Allows to create unit containers from raw values and to apply wrapped funcs.
See also
ClassVar
[Sequence
[Law
]] = (<returns.primitives.laws.Law1 object>, <returns.primitives.laws.Law3 object>, <returns.primitives.laws.Law3 object>, <returns.primitives.laws.Law3 object>)¶Some classes and interfaces might have laws, some might not have any.
Allows to apply a wrapped function over a container.
self (TypeVar
(_ApplicativeType
, bound= ApplicativeN)) –
container (KindN
[TypeVar
(_ApplicativeType
, bound= ApplicativeN), Callable
[[TypeVar
(_FirstType
)], TypeVar
(_UpdatedType
)], TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
KindN
[TypeVar
(_ApplicativeType
, bound= ApplicativeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Type alias for kinds with one type argument.
alias of ApplicativeN
[_FirstType
, NoReturn
, NoReturn
]
Type alias for kinds with two type arguments.
alias of ApplicativeN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of ApplicativeN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: LawSpecDef
Mappable or functor laws.
https://en.wikibooks.org/wiki/Haskell/The_Functor_class#The_functor_laws
Mapping identity over a value must return the value unchanged.
altable (AltableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
None
Mapping twice or mapping a composition is the same thing.
altable (AltableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
first (Callable
[[TypeVar
(_SecondType
)], TypeVar
(_NewType1
)]) –
second (Callable
[[TypeVar
(_NewType1
)], TypeVar
(_NewType2
)]) –
None
Bases: Generic
[_FirstType
, _SecondType
, _ThirdType
], Lawful
[AltableN[_FirstType, _SecondType, _ThirdType]
]
Modifies the second type argument with a pure function.
ClassVar
[Sequence
[Law
]] = (<returns.primitives.laws.Law1 object>, <returns.primitives.laws.Law3 object>)¶Some classes and interfaces might have laws, some might not have any.
Allows to run a pure function over a container.
self (TypeVar
(_AltableType
, bound= AltableN)) –
function (Callable
[[TypeVar
(_SecondType
)], TypeVar
(_UpdatedType
)]) –
KindN
[TypeVar
(_AltableType
, bound= AltableN), TypeVar
(_FirstType
), TypeVar
(_UpdatedType
), TypeVar
(_ThirdType
)]
Bases: MappableN
[_FirstType
, _SecondType
, _ThirdType
], AltableN
[_FirstType
, _SecondType
, _ThirdType
]
Allows to change both types of a container at the same time.
Uses .map
to change first type and .alt
to change second type.
Type alias for kinds with two type arguments.
alias of BiMappableN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of BiMappableN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: LawSpecDef
Laws for SwappableN
type.
Swaaping container twice.
It ensure that we get the initial value back. In other words, swapping twice does nothing.
container (SwappableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
None
Bases: BiMappableN
[_FirstType
, _SecondType
, _ThirdType
], Lawful
[SwappableN[_FirstType, _SecondType, _ThirdType]
]
Interface that allows swapping first and second type values.
Type alias for kinds with two type arguments.
alias of SwappableN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of SwappableN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: Generic
[_FirstType
, _SecondType
, _ThirdType
]
Represents a “context” in which calculations can be executed.
Rescueable
allows you to bind together
a series of calculations while maintaining
the context of that specific container.
In contrast to returns.interfaces.bindable.BinbdaleN
,
works with the second type value.
Applies ‘function’ to the result of a previous calculation.
And returns a new container.
self (TypeVar
(_LashableType
, bound= LashableN)) –
function (Callable
[[TypeVar
(_SecondType
)], KindN
[TypeVar
(_LashableType
, bound= LashableN), TypeVar
(_FirstType
), TypeVar
(_UpdatedType
), TypeVar
(_ThirdType
)]]) –
KindN
[TypeVar
(_LashableType
, bound= LashableN), TypeVar
(_FirstType
), TypeVar
(_UpdatedType
), TypeVar
(_ThirdType
)]
Bases: Generic
[_FirstType
, _SecondType
]
Represents containers that can unwrap and return its wrapped value.
There are no aliases or UnwrappableN
for Unwrappable
interface.
Because it always uses two and just two types.
Not all types can be Unwrappable
because we do require
to raise UnwrapFailedError
if unwrap is not possible.
Custom magic method to unwrap inner value from container.
Should be redefined for ones that actually have values. And for ones that raise an exception for no values.
Note
As a part of the contract, failed unwrap
calls
must raise returns.primitives.exceptions.UnwrapFailedError
exception.
This method is the opposite of failure()
.
self (TypeVar
(_UnwrappableType
, bound= Unwrappable)) –
TypeVar
(_FirstType
)
Custom magic method to unwrap inner value from the failed container.
Note
As a part of the contract, failed failure
calls
must raise returns.primitives.exceptions.UnwrapFailedError
exception.
This method is the opposite of unwrap()
.
self (TypeVar
(_UnwrappableType
, bound= Unwrappable)) –
TypeVar
(_SecondType
)
Bases: LawSpecDef
Container laws.
Definition: https://wiki.haskell.org/Monad_laws Good explanation: https://bit.ly/2Qsi5re
Left identity.
The first law states that if we take a value, put it in a default
context with return and then feed it to a function by using bind
,
it’s the same as just taking the value and applying the function to it.
raw_value (TypeVar
(_FirstType
)) –
container (ContainerN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], KindN
[ContainerN
, TypeVar
(_NewType1
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
None
Right identity.
The second law states that if we have a container value
and we use bind
to feed it to .from_value
,
the result is our original container value.
container (ContainerN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
None
Associativity law.
The final monad law says that when
we have a chain of container functions applications with bind
,
it shouldn’t matter how they’re nested.
container (ContainerN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
first (Callable
[[TypeVar
(_FirstType
)], KindN
[ContainerN
, TypeVar
(_NewType1
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
second (Callable
[[TypeVar
(_NewType1
)], KindN
[ContainerN
, TypeVar
(_NewType2
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
None
Bases: ApplicativeN
[_FirstType
, _SecondType
, _ThirdType
], BindableN
[_FirstType
, _SecondType
, _ThirdType
], Lawful
[ContainerN[_FirstType, _SecondType, _ThirdType]
]
Handy alias for types with .bind
, .map
, and .apply
methods.
Should be a base class for almost any containers you write.
See also
Type alias for kinds with one type argument.
alias of ContainerN
[_FirstType
, NoReturn
, NoReturn
]
Type alias for kinds with two type arguments.
alias of ContainerN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of ContainerN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: LawSpecDef
Failable laws.
We need to be sure that .lash
won’t lash success types.
Bases: ContainerN
[_FirstType
, _SecondType
, _ThirdType
], LashableN
[_FirstType
, _SecondType
, _ThirdType
], Lawful
[FailableN[_FirstType, _SecondType, _ThirdType]
]
Base type for types that can fail.
It is a raw type and should not be used directly.
Use SingleFailableN
and DiverseFailableN
instead.
Type alias for kinds with two type arguments.
alias of FailableN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of FailableN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: LawSpecDef
Single Failable laws.
We need to be sure that .map
and .bind
works correctly for empty
property.
Ensures that you cannot map from the empty property.
container (SingleFailableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_NewFirstType
)]) –
None
Ensures that you cannot bind from the empty property.
container (SingleFailableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], KindN
[SingleFailableN
, TypeVar
(_NewFirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
None
Ensures that you cannot apply from the empty property.
container (SingleFailableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_NewFirstType
)]) –
None
Bases: FailableN
[_FirstType
, _SecondType
, _ThirdType
]
Base type for types that have just only one failed value.
Like Maybe
types where the only failed value is Nothing
.
ClassVar
[Sequence
[Law
]] = (<returns.primitives.laws.Law2 object>, <returns.primitives.laws.Law2 object>, <returns.primitives.laws.Law2 object>)¶Some classes and interfaces might have laws, some might not have any.
This property represents the failed value.
:param self:
:type self: TypeVar
(_SingleFailableType
, bound= SingleFailableN)
Type alias for kinds with two types arguments.
alias of SingleFailableN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of SingleFailableN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: LawSpecDef
Diverse Failable laws.
We need to be sure that .map
, .bind
, .apply
and .alt
works correctly for both success and failure types.
Ensures that you cannot map a failure.
raw_value (TypeVar
(_SecondType
)) –
container (DiverseFailableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_NewFirstType
)]) –
None
Ensures that you cannot bind a failure.
See: https://wiki.haskell.org/Typeclassopedia#MonadFail
raw_value (TypeVar
(_SecondType
)) –
container (DiverseFailableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], KindN
[DiverseFailableN
, TypeVar
(_NewFirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
None
Ensures that you cannot apply a failure.
raw_value (TypeVar
(_SecondType
)) –
container (DiverseFailableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_NewFirstType
)]) –
None
Ensures that you cannot alt a success.
raw_value (TypeVar
(_SecondType
)) –
container (DiverseFailableN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_SecondType
)], TypeVar
(_NewFirstType
)]) –
None
Bases: FailableN
[_FirstType
, _SecondType
, _ThirdType
], SwappableN
[_FirstType
, _SecondType
, _ThirdType
], Lawful
[DiverseFailableN[_FirstType, _SecondType, _ThirdType]
]
Base type for types that have any failed value.
Like Result
types.
ClassVar
[Sequence
[Law
]] = (<returns.primitives.laws.Law3 object>, <returns.primitives.laws.Law3 object>, <returns.primitives.laws.Law3 object>, <returns.primitives.laws.Law3 object>)¶Some classes and interfaces might have laws, some might not have any.
Unit method to create new containers from any raw value.
inner_value (TypeVar
(_UpdatedType
)) –
KindN
[TypeVar
(_DiverseFailableType
, bound= DiverseFailableN), TypeVar
(_FirstType
), TypeVar
(_UpdatedType
), TypeVar
(_ThirdType
)]
Type alias for kinds with two type arguments.
alias of DiverseFailableN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of DiverseFailableN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: LawSpecDef
Maybe laws.
We need to be sure that
.map
, .bind
, .bind_optional
, and .lash
works correctly for both successful and failed types.
Ensures that you cannot map from failures.
container (MaybeLikeN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_NewType1
)]) –
None
Ensures that you cannot bind from failures.
container (MaybeLikeN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], KindN
[MaybeLikeN
, TypeVar
(_NewType1
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
None
Ensures that you cannot bind from failures.
container (MaybeLikeN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], Optional
[TypeVar
(_NewType1
)]]) –
None
Ensures that you cannot lash a success.
raw_value (TypeVar
(_FirstType
)) –
container (MaybeLikeN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_SecondType
)], KindN
[MaybeLikeN
, TypeVar
(_FirstType
), TypeVar
(_NewType1
), TypeVar
(_ThirdType
)]]) –
None
Ensures None
is treated specially.
container (MaybeLikeN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], None
]) –
None
Bases: SingleFailableN
[_FirstType
, _SecondType
, _ThirdType
], Lawful
[MaybeLikeN[_FirstType, _SecondType, _ThirdType]
]
Type for values that do look like a Maybe
.
For example, RequiresContextMaybe
should be created from this interface.
Cannot be unwrapped or compared.
ClassVar
[Sequence
[Law
]] = (<returns.primitives.laws.Law2 object>, <returns.primitives.laws.Law2 object>, <returns.primitives.laws.Law2 object>, <returns.primitives.laws.Law3 object>, <returns.primitives.laws.Law2 object>)¶Some classes and interfaces might have laws, some might not have any.
Binds a function that returns Optional
values.
self (TypeVar
(_MaybeLikeType
, bound= MaybeLikeN)) –
function (Callable
[[TypeVar
(_FirstType
)], Optional
[TypeVar
(_UpdatedType
)]]) –
KindN
[TypeVar
(_MaybeLikeType
, bound= MaybeLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Type alias for kinds with two type arguments.
alias of MaybeLikeN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of MaybeLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: MaybeLikeN
[_FirstType
, _SecondType
, _ThirdType
], Unwrappable
[_FirstType
, None
], Equable
Concrete interface for Maybe
type.
Can be unwrapped and compared.
Type alias for kinds with two type arguments.
alias of MaybeBasedN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of MaybeBasedN
[_FirstType
, _SecondType
, _ThirdType
]
An interface that represents a pure computation result.
For impure result see
returns.interfaces.specific.ioresult.IOResultLikeN
type.
Bases: DiverseFailableN
[_FirstType
, _SecondType
, _ThirdType
]
Base types for types that looks like Result
but cannot be unwrapped.
Like RequiresContextResult
or FutureResult
.
Type alias for kinds with two type arguments.
alias of ResultLikeN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of ResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: ResultLikeN
[_FirstType
, _SecondType
, _ThirdType
], Unwrappable
[_FirstUnwrappableType
, _SecondUnwrappableType
], Equable
Intermediate type with 5 type arguments that represents unwrappable result.
It is a raw type and should not be used directly.
Use ResultBasedN
and IOResultBasedN
instead.
Bases: UnwrappableResult
[_FirstType
, _SecondType
, _ThirdType
, _FirstType
, _SecondType
]
Base type for real Result
types.
Can be unwrapped.
Type alias for kinds with two type arguments.
alias of ResultBasedN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of ResultBasedN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: ContainerN
[_FirstType
, _SecondType
, _ThirdType
]
Represents interface for types that looks like fearless IO
.
This type means that IO
cannot fail. Like random numbers, date, etc.
Don’t use this type for IO
that can. Instead, use
returns.interfaces.specific.ioresult.IOResultBasedN
type.
Type alias for kinds with one type argument.
alias of IOLikeN
[_FirstType
, NoReturn
, NoReturn
]
Type alias for kinds with two type arguments.
alias of IOLikeN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of IOLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: IOLikeN
[_FirstType
, _SecondType
, _ThirdType
], Equable
Represents the base interface for types that do fearless IO
.
This type means that IO
cannot fail. Like random numbers, date, etc.
Don’t use this type for IO
that can. Instead, use
returns.interfaces.specific.ioresult.IOResultBasedN
type.
This interface also supports direct comparison of two values.
While IOLikeN
is different. It can be lazy and cannot be compared.
Type alias for kinds with one type argument.
alias of IOBasedN
[_FirstType
, NoReturn
, NoReturn
]
An interface for types that do IO
and can fail.
It is a base interface for both sync and async IO
stacks.
Bases: IOLikeN
[_FirstType
, _SecondType
, _ThirdType
], ResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Base type for types that look like IOResult
but cannot be unwrapped.
Like FutureResult
or RequiresContextIOResult
.
Runs IOResult
returning function over a container.
self (TypeVar
(_IOResultLikeType
, bound= IOResultLikeN)) –
function (Callable
[[TypeVar
(_FirstType
)], IOResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
)]]) –
KindN
[TypeVar
(_IOResultLikeType
, bound= IOResultLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Allows to compose the underlying Result
with a function.
KindN
[TypeVar
(_IOResultLikeType
, bound= IOResultLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Type alias for kinds with two type arguments.
alias of IOResultLikeN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of IOResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: IOResultLikeN
[_FirstType
, _SecondType
, _ThirdType
], IOBasedN
[_FirstType
, _SecondType
, _ThirdType
], UnwrappableResult
[_FirstType
, _SecondType
, _ThirdType
, IO[_FirstType]
, IO[_SecondType]
]
Base type for real IOResult
types.
Can be unwrapped.
Type alias for kinds with two type arguments.
alias of IOResultBasedN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of IOResultBasedN
[_FirstType
, _SecondType
, _ThirdType
]
Represents the base interfaces for types that do fearless async operations.
This type means that Future
cannot fail.
Don’t use this type for async that can. Instead, use
returns.interfaces.specific.future_result.FutureResultBasedN
type.
Bases: IOLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Base type for ones that does look like Future
.
But at the time this is not a real Future
and cannot be awaited.
Allows to bind async Future
returning function over container.
Binds async function returning the same type of container.
self (TypeVar
(_FutureLikeType
, bound= FutureLikeN)) –
function (Callable
[[TypeVar
(_FirstType
)], Awaitable
[KindN
[TypeVar
(_FutureLikeType
, bound= FutureLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]) –
KindN
[TypeVar
(_FutureLikeType
, bound= FutureLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Allows to bind async function over container.
self (TypeVar
(_FutureLikeType
, bound= FutureLikeN)) –
function (Callable
[[TypeVar
(_FirstType
)], Awaitable
[TypeVar
(_UpdatedType
)]]) –
KindN
[TypeVar
(_FutureLikeType
, bound= FutureLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Type alias for kinds with one type argument.
alias of FutureLikeN
[_FirstType
, NoReturn
, NoReturn
]
Type alias for kinds with two type arguments.
alias of FutureLikeN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of FutureLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: Generic
[_FirstType
, _SecondType
, _ThirdType
]
Type that provides the required API for Future
to be async.
Should not be used directly. Use FutureBasedN
instead.
Type alias for kinds with one type argument.
alias of AwaitableFutureN
[_FirstType
, NoReturn
, NoReturn
]
Type alias for kinds with two type arguments.
alias of AwaitableFutureN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of AwaitableFutureN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: FutureLikeN
[_FirstType
, _SecondType
, _ThirdType
], AwaitableFutureN
[_FirstType
, _SecondType
, _ThirdType
]
Base type for real Future
objects.
They can be awaited.
Type alias for kinds with one type argument.
alias of FutureBasedN
[_FirstType
, NoReturn
, NoReturn
]
Type alias for kinds with two type arguments.
alias of FutureBasedN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of FutureBasedN
[_FirstType
, _SecondType
, _ThirdType
]
Represents the base interfaces for types that do fear-some async operations.
This type means that FutureResult
can (and will!) fail with exceptions.
Use this type to mark that this specific async opetaion can fail.
Bases: FutureLikeN
[_FirstType
, _SecondType
, _ThirdType
], IOResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Base type for ones that does look like FutureResult
.
But at the time this is not a real Future
and cannot be awaited.
It is also cannot be unwrapped, because it is not a real IOResult
.
Allows to bind FutureResult
functions over a container.
self (TypeVar
(_FutureResultLikeType
, bound= FutureResultLikeN)) –
function (Callable
[[TypeVar
(_FirstType
)], FutureResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
)]]) –
KindN
[TypeVar
(_FutureResultLikeType
, bound= FutureResultLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Allows to bind async FutureResult
functions over container.
self (TypeVar
(_FutureResultLikeType
, bound= FutureResultLikeN)) –
function (Callable
[[TypeVar
(_FirstType
)], Awaitable
[FutureResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
)]]]) –
KindN
[TypeVar
(_FutureResultLikeType
, bound= FutureResultLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Creates new container from a failed Future
.
Creates container from FutureResult
instance.
inner_value (FutureResult
[TypeVar
(_ValueType
), TypeVar
(_ErrorType
)]) –
KindN
[TypeVar
(_FutureResultLikeType
, bound= FutureResultLikeN), TypeVar
(_ValueType
), TypeVar
(_ErrorType
), TypeVar
(_ThirdType
)]
Type alias for kinds with two type arguments.
alias of FutureResultLikeN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of FutureResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: FutureBasedN
[_FirstType
, _SecondType
, _ThirdType
], FutureResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Base type for real FutureResult
objects.
They can be awaited. Still cannot be unwrapped.
Type alias for kinds with two type arguments.
alias of FutureResultBasedN
[_FirstType
, _SecondType
, NoReturn
]
Type alias for kinds with three type arguments.
alias of FutureResultBasedN
[_FirstType
, _SecondType
, _ThirdType
]
This module is special.
Reader
does not produce ReaderLikeN
interface as other containers.
Because Reader
can be used with two or three type arguments:
- RequiresContext[value, env]
- RequiresContextResult[value, error, env]
Because the second type argument changes its meaning
based on the used KindN
instance,
we need to have two separate interfaces for two separate use-cases:
- ReaderLike2
is used for types where the second type argument is env
- ReaderLike3
is used for types where the third type argument is env
We also have two methods and two poinfree helpers
for bind_context
composition: one for each interface.
Furthermore, Reader
cannot have ReaderLike1
type,
because we need both value
and env
types at all cases.
Bases: Generic
[_ValueType
, _EnvType
]
Special type we use as a base one for all callble Reader
instances.
It only has a single method. And is a base type for every single one of them.
But, each Reader
defines the return type differently.
For example:
Reader
has just _ReturnType
ReaderResult
has Result[_FirstType, _SecondType]
ReaderIOResult
has IOResult[_FirstType, _SecondType]
And so on.
Bases: ContainerN
[_FirstType
, _SecondType
, NoReturn
]
Reader interface for Kind2
based types.
It has two type arguments and treats the second type argument as env type.
Is required to call Reader
with no explicit arguments.
:param self:
:type self: TypeVar
(_ReaderLike2Type
, bound= ReaderLike2)
Allows to apply a wrapped function over a Reader
container.
self (TypeVar
(_ReaderLike2Type
, bound= ReaderLike2)) –
function (Callable
[[TypeVar
(_FirstType
)], RequiresContext
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
)]]) –
KindN
[TypeVar
(_ReaderLike2Type
, bound= ReaderLike2), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), Any
]
Transforms the environment before calling the container.
self (TypeVar
(_ReaderLike2Type
, bound= ReaderLike2)) –
function (Callable
[[TypeVar
(_UpdatedType
)], TypeVar
(_SecondType
)]) –
KindN
[TypeVar
(_ReaderLike2Type
, bound= ReaderLike2), TypeVar
(_FirstType
), TypeVar
(_UpdatedType
), Any
]
Returns the dependencies inside the container.
KindN
[TypeVar
(_ReaderLike2Type
, bound= ReaderLike2), TypeVar
(_SecondType
), TypeVar
(_SecondType
), Any
]
Unit method to create new containers from successful Reader
.
inner_value (RequiresContext
[TypeVar
(_ValueType
), TypeVar
(_EnvType
)]) –
KindN
[TypeVar
(_ReaderLike2Type
, bound= ReaderLike2), TypeVar
(_ValueType
), TypeVar
(_EnvType
), Any
]
Bases: ReaderLike2
[_FirstType
, _SecondType
], Contextable
[_ValueType
, _EnvType
]
Intermediate interface for ReaderLike2
+ __call__
method.
Has 4 type variables to type Reader
and __call__
independently.
Since, we don’t have any other fancy ways of doing it.
Should not be used directly
other than defining your own Reader
interfaces.
Bases: ContainerN
[_FirstType
, _SecondType
, _ThirdType
]
Reader interface for Kind3
based types.
It has three type arguments and treats the third type argument as env type. The second type argument is not used here.
Is required to call Reader
with no explicit arguments.
:param self:
:type self: TypeVar
(_ReaderLike3Type
, bound= ReaderLike3)
Allows to apply a wrapped function over a Reader
container.
self (TypeVar
(_ReaderLike3Type
, bound= ReaderLike3)) –
function (Callable
[[TypeVar
(_FirstType
)], RequiresContext
[TypeVar
(_UpdatedType
), TypeVar
(_ThirdType
)]]) –
KindN
[TypeVar
(_ReaderLike3Type
, bound= ReaderLike3), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Transforms the environment before calling the container.
self (TypeVar
(_ReaderLike3Type
, bound= ReaderLike3)) –
function (Callable
[[TypeVar
(_UpdatedType
)], TypeVar
(_ThirdType
)]) –
KindN
[TypeVar
(_ReaderLike3Type
, bound= ReaderLike3), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_UpdatedType
)]
Returns the dependencies inside the container.
KindN
[TypeVar
(_ReaderLike3Type
, bound= ReaderLike3), TypeVar
(_ThirdType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Unit method to create new containers from successful Reader
.
inner_value (RequiresContext
[TypeVar
(_ValueType
), TypeVar
(_EnvType
)]) –
KindN
[TypeVar
(_ReaderLike3Type
, bound= ReaderLike3), TypeVar
(_ValueType
), TypeVar
(_SecondType
), TypeVar
(_EnvType
)]
Bases: ReaderLike3
[_FirstType
, _SecondType
, _ThirdType
], Contextable
[_ValueType
, _EnvType
]
Intermediate interface for ReaderLike3
+ __call__
method.
Has 5 type variables to type Reader
and __call__
independently.
Since, we don’t have any other fancy ways of doing it.
Should not be used directly
other than defining your own Reader
interfaces.
Bases: LawSpecDef
Concrete laws for ReaderBased2
.
See: https://github.com/haskell/mtl/pull/61/files
Calling a Reader
twice has the same result with the same env.
container (ReaderBased2
[TypeVar
(_FirstType
), TypeVar
(_SecondType
)]) –
env (TypeVar
(_SecondType
)) –
None
Asking for an env, always returns the env.
container (ReaderBased2
[TypeVar
(_FirstType
), TypeVar
(_SecondType
)]) –
env (TypeVar
(_SecondType
)) –
None
Bases: CallableReader2
[_FirstType
, _SecondType
, _FirstType
, _SecondType
], Lawful
[ReaderBased2[_FirstType, _SecondType]
]
This interface is very specific to our Reader
type.
The only thing that differs from ReaderLike2
is that we know
the specific types for its __call__
method.
Bases: ReaderLike3
[_FirstType
, _SecondType
, _ThirdType
], ResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Base interface for all types that do look like ReaderResult
instance.
Cannot be called.
Binds a ReaderResult
returning function over a container.
self (TypeVar
(_ReaderResultLikeType
, bound= ReaderResultLikeN)) –
function (Callable
[[TypeVar
(_FirstType
)], RequiresContextResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
KindN
[TypeVar
(_ReaderResultLikeType
, bound= ReaderResultLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Unit method to create new containers from failed Reader
.
inner_value (RequiresContext
[TypeVar
(_ErrorType
), TypeVar
(_EnvType
)]) –
KindN
[TypeVar
(_ReaderResultLikeType
, bound= ReaderResultLikeN), TypeVar
(_FirstType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
)]
Unit method to create new containers from ReaderResult
.
inner_value (RequiresContextResult
[TypeVar
(_ValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
)]) –
KindN
[TypeVar
(_ReaderResultLikeType
, bound= ReaderResultLikeN), TypeVar
(_ValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
)]
Type alias for kinds with three type arguments.
alias of ReaderResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: LawSpecDef
Concrete laws for ReaderResulBasedN
.
See: https://github.com/haskell/mtl/pull/61/files
Calling a Reader
twice has the same result with the same env.
container (ReaderResultBasedN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
env (TypeVar
(_ThirdType
)) –
None
Asking for an env, always returns the env.
container (ReaderResultBasedN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
env (TypeVar
(_ThirdType
)) –
None
Bases: ReaderResultLikeN
[_FirstType
, _SecondType
, _ThirdType
], CallableReader3
[_FirstType
, _SecondType
, _ThirdType
, Result[_FirstType, _SecondType]
, _ThirdType
], Lawful
[ReaderResultBasedN[_FirstType, _SecondType, _ThirdType]
]
This interface is very specific to our ReaderResult
type.
The only thing that differs from ReaderResultLikeN
is that we know
the specific types for its __call__
method.
In this case the return type of __call__
is Result
.
Type alias for kinds with three type arguments.
alias of ReaderResultBasedN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: ReaderResultLikeN
[_FirstType
, _SecondType
, _ThirdType
], IOResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Base interface for all types that do look like ReaderIOResult
instance.
Cannot be called.
Binds a ReaderIOResult
returning function over a container.
self (TypeVar
(_ReaderIOResultLikeType
, bound= ReaderIOResultLikeN)) –
function (Callable
[[TypeVar
(_FirstType
)], RequiresContextIOResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
KindN
[TypeVar
(_ReaderIOResultLikeType
, bound= ReaderIOResultLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Unit method to create new containers from ReaderIOResult
.
inner_value (RequiresContextIOResult
[TypeVar
(_ValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
)]) –
KindN
[TypeVar
(_ReaderIOResultLikeType
, bound= ReaderIOResultLikeN), TypeVar
(_ValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
)]
Type alias for kinds with three type arguments.
alias of ReaderIOResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: LawSpecDef
Concrete laws for ReaderIOResultBasedN
.
See: https://github.com/haskell/mtl/pull/61/files
Asking for an env, always returns the env.
container (ReaderIOResultBasedN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
env (TypeVar
(_ThirdType
)) –
None
Bases: ReaderIOResultLikeN
[_FirstType
, _SecondType
, _ThirdType
], CallableReader3
[_FirstType
, _SecondType
, _ThirdType
, IOResult[_FirstType, _SecondType]
, _ThirdType
], Lawful
[ReaderIOResultBasedN[_FirstType, _SecondType, _ThirdType]
]
This interface is very specific to our ReaderIOResult
type.
The only thing that differs from ReaderIOResultLikeN
is that we know
the specific types for its __call__
method.
In this case the return type of __call__
is IOResult
.
Type alias for kinds with three type arguments.
alias of ReaderIOResultBasedN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: ReaderIOResultLikeN
[_FirstType
, _SecondType
, _ThirdType
], FutureResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Interface for all types that do look like ReaderFutureResult
instance.
Cannot be called.
Bind a ReaderFutureResult
returning function over a container.
self (TypeVar
(_ReaderFutureResultLikeType
, bound= ReaderFutureResultLikeN)) –
function (Callable
[[TypeVar
(_FirstType
)], RequiresContextFutureResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
KindN
[TypeVar
(_ReaderFutureResultLikeType
, bound= ReaderFutureResultLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Bind async ReaderFutureResult
function.
self (TypeVar
(_ReaderFutureResultLikeType
, bound= ReaderFutureResultLikeN)) –
function (Callable
[[TypeVar
(_FirstType
)], Awaitable
[RequiresContextFutureResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]) –
KindN
[TypeVar
(_ReaderFutureResultLikeType
, bound= ReaderFutureResultLikeN), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Unit method to create new containers from ReaderFutureResult
.
inner_value (RequiresContextFutureResult
[TypeVar
(_ValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
)]) –
KindN
[TypeVar
(_ReaderFutureResultLikeType
, bound= ReaderFutureResultLikeN), TypeVar
(_ValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
)]
Type alias for kinds with three type arguments.
alias of ReaderFutureResultLikeN
[_FirstType
, _SecondType
, _ThirdType
]
Bases: LawSpecDef
Concrete laws for ReaderFutureResultBasedN
.
See: https://github.com/haskell/mtl/pull/61/files
Asking for an env, always returns the env.
container (ReaderFutureResultBasedN
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
env (TypeVar
(_ThirdType
)) –
None
Bases: ReaderFutureResultLikeN
[_FirstType
, _SecondType
, _ThirdType
], CallableReader3
[_FirstType
, _SecondType
, _ThirdType
, FutureResult[_FirstType, _SecondType]
, _ThirdType
], Lawful
[ReaderFutureResultBasedN[_FirstType, _SecondType, _ThirdType]
]
This interface is very specific to our ReaderFutureResult
type.
The only thing that differs from ReaderFutureResultLikeN
is that we know the specific types for its __call__
method.
In this case the return type of __call__
is FutureResult
.
Type alias for kinds with three type arguments.
alias of ReaderFutureResultBasedN
[_FirstType
, _SecondType
, _ThirdType
]
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
>>> assert str(Maybe.from_optional(1)) == '<Some: 1>'
>>> assert str(Maybe.from_optional(None)) == '<Nothing>'
We also have another method called .from_value
that behaves a bit differently:
>>> from returns.maybe import Maybe
>>> assert str(Maybe.from_value(1)) == '<Some: 1>'
>>> assert str(Maybe.from_value(None)) == '<Some: None>'
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, Nothing
>>> @dataclass
... class Address(object):
... street: Optional[str]
>>> @dataclass
... class User(object):
... address: Optional[Address]
>>> @dataclass
... class Order(object):
... user: Optional[User]
>>> def get_street_address(order: Order) -> Maybe[str]:
... return Maybe.from_optional(order.user).bind_optional(
... lambda user: user.address,
... ).bind_optional(
... 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_address(with_address)) # all fields are not None
'<Some: Some street>'
>>> assert get_street_address(empty_user) == Nothing
>>> assert get_street_address(empty_address) == Nothing
>>> assert get_street_address(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.
Maybe
values can be matched using the new feature of Python 3.10,
Structural Pattern Matching,
see the example below:
from dataclasses import dataclass
from typing import Final
from returns.maybe import Maybe, Nothing, Some
@dataclass
class _Book(object):
book_id: int
name: str
_BOOK_LIST: Final = (
_Book(book_id=1, name='Category Theory for Programmers'),
_Book(book_id=2, name='Fluent Python'),
_Book(book_id=3, name='Learn You Some Erlang for Great Good'),
_Book(book_id=4, name='Learn You a Haskell for Great Good'),
)
def _find_book(book_id: int) -> Maybe[_Book]:
for book in _BOOK_LIST:
if book.book_id == book_id:
return Some(book)
return Nothing
if __name__ == '__main__':
desired_book = _find_book(2)
match desired_book:
# Matches any `Some` instance that contains a book named `Fluent Python`
case Some(_Book(name='Fluent Python')):
print('"Fluent Python" was found')
# Matches any `Some` instance and binds its value to the `book` variable
case Some(book):
print('Book found: {0}'.format(book.name))
# Matches `Nothing` instance
case Maybe.empty:
print('Not found the desired book!')
Typing will only work correctly if our mypy 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, Some, maybe
>>> @maybe
... def number(num: int) -> Optional[int]:
... if num > 0:
... return num
... return None
>>> result: Maybe[int] = number(1)
>>> assert 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.from_optional(1).value_or(None) == 1
>>> assert Maybe.from_optional(None).value_or(None) == None
As you can see, revealed type of .value_or(None)
is Optional[a]
.
Use it a fallback.
Let’s say you have this dict
: values = {'a': 1, 'b': None}
So, you can have two types of None
here:
values.get('b')
values.get('c')
But, they are different!
The first has explicit None
value,
the second one has no given key and None
is used as a default.
You might need to know exactly which case you are dealing with.
For example, in validation.
So, the first thing to remember is that:
>>> assert Some(None) != Nothing
There’s a special way to work with a type like this:
>>> values = {'a': 1, 'b': None}
>>> assert Maybe.from_value(values).map(lambda d: d.get('a')) == Some(1)
>>> assert Maybe.from_value(values).map(lambda d: d.get('b')) == Some(None)
In contrast, you can ignore both None
values easily:
>>> assert Maybe.from_value(values).bind_optional(
... lambda d: d.get('a'),
... ) == Some(1)
>>> assert Maybe.from_value(values).bind_optional(
... lambda d: d.get('b'),
... ) == Nothing
So, how to write a complete check for a value: both present and missing?
>>> from typing import Optional, Dict, TypeVar
>>> from returns.maybe import Maybe, Some, Nothing
>>> _Key = TypeVar('_Key')
>>> _Value = TypeVar('_Value')
>>> def check_key(
... heystack: Dict[_Key, _Value],
... needle: _Key,
... ) -> Maybe[_Value]:
... if needle not in heystack:
... return Nothing
... return Maybe.from_value(heystack[needle]) # try with `.from_optional`
>>> real_values = {'a': 1}
>>> opt_values = {'a': 1, 'b': None}
>>> assert check_key(real_values, 'a') == Some(1)
>>> assert check_key(real_values, 'b') == Nothing
>>> # Type revealed: returns.maybe.Maybe[builtins.int]
>>> assert check_key(opt_values, 'a') == Some(1)
>>> assert check_key(opt_values, 'b') == Some(None)
>>> assert check_key(opt_values, 'c') == Nothing
>>> # Type revealed: returns.maybe.Maybe[Union[builtins.int, None]]
Choose wisely between .from_value
and .map
,
and .from_optional
and .bind_optional
.
They are similar, but do different things.
Note that you can also use returns.pipeline.is_successful()
to check if the value is present.
See the original issue about Some(None) for more details and the full history.
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.
Well, because Maybe
only has a single failed value:
Nothing
and it cannot be altered.
But, Maybe
has returns.maybe.Maybe.or_else_call()
method to call
a passed callback function with zero argument on failed container:
>>> from returns.maybe import Some, Nothing
>>> assert Some(1).or_else_call(lambda: 2) == 1
>>> assert Nothing.or_else_call(lambda: 2) == 2
This method is unique to Maybe
container.
Bases: BaseContainer
, SupportsKindN
[Maybe
, _ValueType
, NoReturn
, NoReturn
], MaybeBasedN
[_ValueType
, None
, NoReturn
]
Represents a result of a series of computations 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
.
ClassVar
[Maybe[Any]] = <Nothing>¶Alias for Nothing
Success type that is used to represent the successful computation.
inner_value (TypeVar
(_ValueType
, covariant=True)) –
alias of Some
Failure type that is used to represent the failed computation.
inner_value (None
) –
alias of _Nothing
Typesafe equality comparison with other Result objects.
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
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)]) –
Maybe
[TypeVar
(_NewValueType
)]
Calls a wrapped function in a container on this container.
>>> from returns.maybe import Some, Nothing
>>> def appliable(string: str) -> str:
... return string + 'b'
>>> assert Some('a').apply(Some(appliable)) == Some('ab')
>>> assert Some('a').apply(Nothing) == Nothing
>>> assert Nothing.apply(Some(appliable)) == Nothing
>>> assert Nothing.apply(Nothing) == Nothing
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
Binds a function returning an optional value over a container.
>>> from returns.maybe import Some, Nothing
>>> from typing import Optional
>>> def bindable(arg: str) -> Optional[int]:
... return len(arg) if arg else None
>>> assert Some('a').bind_optional(bindable) == Some(1)
>>> assert Some('').bind_optional(bindable) == Nothing
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Optional
[TypeVar
(_NewValueType
)]]) –
Maybe
[TypeVar
(_NewValueType
)]
Composes failed container with a function that returns a container.
>>> from returns.maybe import Maybe, Some, Nothing
>>> def lashable(arg=None) -> Maybe[str]:
... return Some('b')
>>> assert Some('a').lash(lashable) == Some('a')
>>> assert Nothing.lash(lashable) == Some('b')
We need this feature to make Maybe
compatible
with different Result
like operations.
Allows working with unwrapped values of containers in a safe way.
>>> from returns.maybe import Maybe, Some, Nothing
>>> assert Maybe.do(
... first + second
... for first in Some(2)
... for second in Some(3)
... ) == Some(5)
>>> assert Maybe.do(
... first + second
... for first in Some(2)
... for second in Nothing
... ) == Nothing
See Do Notation to learn more.
expr (Generator
[TypeVar
(_NewValueType
), None
, None
]) –
Maybe
[TypeVar
(_NewValueType
)]
Get value from successful 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
default_value (TypeVar
(_NewValueType
)) –
Union
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewValueType
)]
Get value from successful container or default value from failed one.
Really close to value_or()
but works with lazy values.
This method is unique to Maybe
container, because other containers
do have .alt
method.
But, Maybe
does not have this method.
There’s nothing to alt
in Nothing
.
Instead, it has this method to execute some function if called on a failed container:
>>> from returns.maybe import Some, Nothing
>>> assert Some(1).or_else_call(lambda: 2) == 1
>>> assert Nothing.or_else_call(lambda: 2) == 2
It might be useful to work with exceptions as well:
>>> def fallback() -> NoReturn:
... raise ValueError('Nothing!')
>>> Nothing.or_else_call(fallback)
Traceback (most recent call last):
...
ValueError: Nothing!
function (Callable
[[], TypeVar
(_NewValueType
)]) –
Union
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewValueType
)]
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
TypeVar
(_ValueType
, covariant=True)
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
Creates new instance of Maybe
container based on a value.
>>> from returns.maybe import Maybe, Some
>>> assert Maybe.from_value(1) == Some(1)
>>> assert Maybe.from_value(None) == Some(None)
inner_value (TypeVar
(_NewValueType
)) –
Maybe
[TypeVar
(_NewValueType
)]
Creates new instance of Maybe
container based on an optional value.
>>> from returns.maybe import Maybe, Some, Nothing
>>> assert Maybe.from_optional(1) == Some(1)
>>> assert Maybe.from_optional(None) == Nothing
inner_value (Optional
[TypeVar
(_NewValueType
)]) –
Maybe
[TypeVar
(_NewValueType
)]
Bases: Maybe
[_ValueType
]
Represents a calculation which has succeeded and contains the value.
Quite similar to Success
type.
inner_value (TypeVar
(_ValueType
, covariant=True)) –
Decorator to convert None
-returning function to Maybe
container.
This decorator works with sync functions only. Example:
>>> 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)
function (Callable
[[ParamSpec
(_FuncParams
)], Optional
[TypeVar
(_ValueType
, covariant=True)]]) –
Callable
[[ParamSpec
(_FuncParams
)], Maybe
[TypeVar
(_ValueType
, covariant=True)]]
Make sure to get familiar with Railway oriented programming.
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
flow
or pipe!
Read more about them if you want to compose your containers easily.
Result
values can be matched using the new feature of Python 3.10,
Structural Pattern Matching,
see the example below:
from returns.result import Failure, Success, safe
@safe
def div(first_number: int, second_number: int) -> int:
return first_number // second_number
match div(1, 0):
# Matches if the result stored inside `Success` is `10`
case Success(10):
print('Result is "10"')
# Matches any `Success` instance and binds its value to the `value` variable
case Success(value):
print('Result is "{0}"'.format(value))
# Matches if the result stored inside `Failure` is `ZeroDivisionError`
case Failure(ZeroDivisionError()):
print('"ZeroDivisionError" was raised')
# Matches any `Failure` instance
case Failure(_):
print('The division was a failure')
There are several useful alises for Result
type with some common values:
returns.result.ResultE
is an alias for Result[... Exception]
,
just use it when you want to work with Result
containers
that use exceptions as error type.
It is named ResultE
because it is ResultException
and ResultError
at the same time.
Typing will only work correctly if our mypy plugin is used. This happens due to mypy issue.
safe
is used to convert
regular functions that can throw exceptions to functions
that return Result
type.
Supports only regular functions.
If you need to mark async
functions as safe
,
use future_safe
instead.
>>> from returns.result import Success, safe
>>> @safe # Will convert type to: Callable[[int], Result[float, Exception]]
... def divide(number: int) -> float:
... return number / number
>>> assert divide(1) == Success(1.0)
>>> str(divide(0))
'<Failure: division by zero>'
If you want to safe handle only a set of exceptions:
>>> @safe(exceptions=(ZeroDivisionError,)) # Other exceptions will be raised
... def divide(number: int) -> float:
... if number > 10:
... raise ValueError('Too big')
... return number / number
>>> assert divide(5) == Success(1.0)
>>> assert divide(0).failure()
>>> divide(15)
Traceback (most recent call last):
...
ValueError: Too big
Similar to safe
function but instead
of wrapping the exception error in a Failure
container it’ll wrap the
argument that lead to that exception.
>>> from returns.result import Failure, Success, attempt
>>> @attempt
... def divide_itself(number: int) -> float:
... return number / number
>>> assert divide_itself(2) == Success(1.0)
>>> assert divide_itself(0) == Failure(0)
Warning
This decorator works only with functions that has just one argument.
Use Success
or Failure
.
Alternatively returns.result.Result.from_value()
or returns.result.Result.from_failure()
.
It might be a good idea to use unit functions together with the explicit annotation. Python’s type system does not allow us to do much, so this is required:
>>> from returns.result import Result, Success
>>> def callback(arg: int) -> Result[float, int]:
... return Success(float(arg))
>>> first: Result[int, int] = Success(1)
>>> assert first.bind(callback) == Success(1.0)
Otherwise first
will have Result[int, Any]
type.
Which is okay in some situations.
You might want to sometimes use unify
Pointfree functions
instead of .bind
to compose error types together.
While .bind
enforces error type to stay the same,
unify
is designed
to return a Union
of a revious error type and a new one.
It gives an extra flexibility, but also provokes more thinking and can be problematic in some cases.
Like so:
>>> from returns.result import Result, Success, Failure
>>> from returns.pointfree import unify
>>> def div(number: int) -> Result[float, ZeroDivisionError]:
... if number:
... return Success(1 / number)
... return Failure(ZeroDivisionError('division by zero'))
>>> container: Result[int, ValueError] = Success(1)
>>> assert unify(div)(container) == Success(1.0)
>>> # => Revealed type is:
>>> # Result[float, Union[ValueError, ZeroDivisionError]]
So, that’s a way to go, if you need this composition.
We use the map
method when we’re working with pure functions, a function
is pure if it doesn’t produce any side-effect (e.g. Exceptions). On the other
hand, we use the bind
method if a function returns a Result
instance
which translates its potential side-effect into a raw value.
See the example below:
>>> import json
>>> from typing import Dict
>>> from returns.result import Failure, Result, Success, safe
>>> # `cast_to_bool` doesn't produce any side-effect
>>> def cast_to_bool(arg: int) -> bool:
... return bool(arg)
>>> # `parse_json` can produce Exceptions, so we use the `safe` decorator
>>> # to prevent any kind of exceptions
>>> @safe
... def parse_json(arg: str) -> Dict[str, str]:
... return json.loads(arg)
>>> assert Success(1).map(cast_to_bool) == Success(True)
>>> assert Success('{"example": "example"}').bind(parse_json) == Success({"example": "example"})
>>> assert Success('').bind(parse_json).alt(str) == Failure('Expecting value: line 1 column 1 (char 0)')
Result
is a container and you can use returns.pipeline.is_successful()
like so:
>>> from returns.result import Success, Failure
>>> from returns.pipeline import is_successful
>>> assert is_successful(Success(1)) is True
>>> assert is_successful(Failure('text')) is False
Bases: BaseContainer
, SupportsKindN
[Result
, _ValueType
, _ErrorType
, NoReturn
], ResultBasedN
[_ValueType
, _ErrorType
, NoReturn
]
Base class for Failure
and Success
.
Result
does not have a public constructor.
Use Success()
and Failure()
to construct the needed values.
Success type that is used to represent the successful computation.
inner_value (TypeVar
(_ValueType
, covariant=True)) –
alias of Success
Failure type that is used to represent the failed computation.
inner_value (TypeVar
(_ErrorType
, covariant=True)) –
alias of Failure
Typesafe equality comparison with other Result objects.
Returns a list with stack trace when Failure()
was called.
Swaps value and error types.
So, values become errors and errors become values.
It is useful when you have to work with errors a lot.
And since we have a lot of .bind_
related methods
and only a single .lash
- it is easier to work with values.
>>> from returns.result import Success, Failure
>>> assert Success(1).swap() == Failure(1)
>>> assert Failure(1).swap() == Success(1)
Result
[TypeVar
(_ErrorType
, covariant=True), TypeVar
(_ValueType
, covariant=True)]
Composes successful container with a pure function.
>>> from returns.result import Failure, Success
>>> def mappable(string: str) -> str:
... return string + 'b'
>>> assert Success('a').map(mappable) == Success('ab')
>>> assert Failure('a').map(mappable) == Failure('a')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)]) –
Result
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Calls a wrapped function in a container on this container.
>>> from returns.result import Failure, Success
>>> def appliable(string: str) -> str:
... return string + 'b'
>>> assert Success('a').apply(Success(appliable)) == Success('ab')
>>> assert Failure('a').apply(Success(appliable)) == Failure('a')
>>> assert Success('a').apply(Failure(1)) == Failure(1)
>>> assert Failure(1).apply(Failure(2)) == Failure(1)
Composes successful container with a function that returns a container.
>>> from returns.result import Result, Success, Failure
>>> def bindable(arg: str) -> Result[str, str]:
... if len(arg) > 1:
... return Success(arg + 'b')
... return Failure(arg + 'c')
>>> assert Success('aa').bind(bindable) == Success('aab')
>>> assert Success('a').bind(bindable) == Failure('ac')
>>> assert Failure('a').bind(bindable) == Failure('a')
Alias for bind_result method, it is the same as bind here.
Composes failed container with a pure function to modify failure.
>>> from returns.result import Failure, Success
>>> def altable(arg: str) -> str:
... return arg + 'b'
>>> assert Success('a').alt(altable) == Success('a')
>>> assert Failure('a').alt(altable) == Failure('ab')
function (Callable
[[TypeVar
(_ErrorType
, covariant=True)], TypeVar
(_NewErrorType
)]) –
Result
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
)]
Composes failed container with a function that returns a container.
>>> from returns.result import Result, Success, Failure
>>> def lashable(arg: str) -> Result[str, str]:
... if len(arg) > 1:
... return Success(arg + 'b')
... return Failure(arg + 'c')
>>> assert Success('a').lash(lashable) == Success('a')
>>> assert Failure('a').lash(lashable) == Failure('ac')
>>> assert Failure('aa').lash(lashable) == Success('aab')
Allows working with unwrapped values of containers in a safe way.
>>> from returns.result import Result, Failure, Success
>>> assert Result.do(
... first + second
... for first in Success(2)
... for second in Success(3)
... ) == Success(5)
>>> assert Result.do(
... first + second
... for first in Failure('a')
... for second in Success(3)
... ) == Failure('a')
See Do Notation to learn more. This feature requires our mypy plugin.
expr (Generator
[TypeVar
(_NewValueType
), None
, None
]) –
Result
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]
Get value or default value.
>>> from returns.result import Failure, Success
>>> assert Success(1).value_or(2) == 1
>>> assert Failure(1).value_or(2) == 2
default_value (TypeVar
(_NewValueType
)) –
Union
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewValueType
)]
Get value or raise exception.
>>> from returns.result import Failure, Success
>>> assert Success(1).unwrap() == 1
>>> Failure(1).unwrap()
Traceback (most recent call last):
...
returns.primitives.exceptions.UnwrapFailedError
TypeVar
(_ValueType
, covariant=True)
Get failed value or raise exception.
>>> from returns.result import Failure, Success
>>> assert Failure(1).failure() == 1
>>> Success(1).failure()
Traceback (most recent call last):
...
returns.primitives.exceptions.UnwrapFailedError
TypeVar
(_ErrorType
, covariant=True)
One more value to create success unit values.
It is useful as a united way to create a new value from any container.
>>> from returns.result import Result, Success
>>> assert Result.from_value(1) == Success(1)
You can use this method or Success()
,
choose the most convenient for you.
inner_value (TypeVar
(_NewValueType
)) –
Result
[TypeVar
(_NewValueType
), Any
]
One more value to create failure unit values.
It is useful as a united way to create a new value from any container.
>>> from returns.result import Result, Failure
>>> assert Result.from_failure(1) == Failure(1)
You can use this method or Failure()
,
choose the most convenient for you.
inner_value (TypeVar
(_NewErrorType
)) –
Result
[Any
, TypeVar
(_NewErrorType
)]
Creates a new Result
instance from existing Result
instance.
>>> from returns.result import Result, Failure, Success
>>> assert Result.from_result(Success(1)) == Success(1)
>>> assert Result.from_result(Failure(1)) == Failure(1)
This is a part of
returns.interfaces.specific.result.ResultBasedN
interface.
Bases: Result
[Any
, _ErrorType
]
Represents a calculation which has failed.
It should contain an error code or message.
inner_value (TypeVar
(_ErrorType
, covariant=True)) –
Alias for bind method. Part of the ResultBasedN interface.
Bases: Result
[_ValueType
, Any
]
Represents a calculation which has succeeded and contains the result.
Contains the computation value.
inner_value (TypeVar
(_ValueType
, covariant=True)) –
Alias for bind method. Part of the ResultBasedN interface.
Alias for a popular case when Result
has Exception
as error type.
alias of Result
[_ValueType
, Exception
]
Decorator to convert exception-throwing function to Result
container.
Should be used with care, since it only catches Exception
subclasses.
It does not catch BaseException
subclasses.
If you need to mark async
function as safe
,
use returns.future.future_safe()
instead.
This decorator only works with sync functions. Example:
>>> from returns.result import Result, Success, safe
>>> @safe
... def might_raise(arg: int) -> float:
... return 1 / arg
>>> assert might_raise(1) == Success(1.0)
>>> assert isinstance(might_raise(0), Result.failure_type)
You can also use it with explicit exception types as the first argument:
>>> from returns.result import Result, Success, safe
>>> @safe(exceptions=(ZeroDivisionError,))
... def might_raise(arg: int) -> float:
... return 1 / arg
>>> assert might_raise(1) == Success(1.0)
>>> assert isinstance(might_raise(0), Result.failure_type)
In this case, only exceptions that are explicitly listed are going to be caught.
Similar to returns.io.impure_safe()
and returns.future.future_safe()
decorators.
function (Optional
[Callable
[[ParamSpec
(_FuncParams
)], TypeVar
(_ValueType
, covariant=True)]]) –
exceptions (Optional
[Tuple
[Type
[Exception
], ...
]]) –
Union
[Callable
[[ParamSpec
(_FuncParams
)], Result
[TypeVar
(_ValueType
, covariant=True), Exception
]], Callable
[[Callable
[[ParamSpec
(_FuncParams
)], TypeVar
(_ValueType
, covariant=True)]], Callable
[[ParamSpec
(_FuncParams
)], Result
[TypeVar
(_ValueType
, covariant=True), Exception
]]]]
Decorator to convert exception-throwing function to Result
container.
It’s very similar with returns.result.safe()
, the difference is when
an exception is raised it won’t wrap that given exception into a Failure,
it’ll wrap the argument that lead to the exception.
>>> import json
>>> from typing import Dict, Any
>>> from returns.result import Failure, Success, attempt
>>> @attempt
... def parse_json(string: str) -> Dict[str, Any]:
... return json.loads(string)
>>> assert parse_json('{"key": "value"}') == Success({'key': 'value'})
>>> assert parse_json('incorrect input') == Failure('incorrect input')
func (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_NewValueType
)]) –
Callable
[[TypeVar
(_FirstType
)], Result
[TypeVar
(_NewValueType
), TypeVar
(_FirstType
)]]
Mathematicians dream in pure functions. Each of them only relies on its arguments and always produces the same result for the same input.
That’s not how a useful program works. We need to rely on the environment and we need to do side effects.
Furthermore, there are several types of IO
in our programs:
Some IO
never fails, like:
getting current date and time, random number, or OS name
Some IO
might fail, like:
sending network requests, accessing filesystem, or database
There’s a solution.
Once you have an IO
operation you can mark it appropriately.
We can use a simple class returns.io.IO
to mark impure parts of the program that do not fail.
>>> import random
>>> from returns.io import IO
>>> def get_random_number() -> IO[int]:
... return IO(random.randint(1, 10))
...
>>> assert isinstance(get_random_number(), IO)
And later we can work inside this IO
context
and do not break into our pure part of the program:
>>> assert get_random_number().map(lambda number: number / number) == IO(1.0)
And it infects all other functions that call it.
>>> def modify_number(number: int) -> IO[float]:
... return get_random_number().map(lambda rnd: number / rnd)
...
>>> assert isinstance(modify_number(1), IO)
It is good enough to indicate that you are aware of side effects of the function.
On the other hand, we can have IO
parts of the program that do fail.
Imagine we have this beautiful pure function:
def can_book_seats(
number_of_seats: int,
reservation: 'Reservation',
) -> bool:
return reservation.capacity >= number_of_seats + reservation.booked
What’s good about it? We can test it easily. Even without setting up any testing framework, simple doctests will be enough.
This code is beautiful, because it is simple.
We can later use its result to process the result of the booking request:
def process_booking_result(is_successful: bool) -> 'ProcessID':
...
process_booking_result(is_successful) # works just fine!
At this point we don’t have IO
in our program.
But, imagine that our requirements had changed. And now we have to grab the number of already booked tickets from some other microservice and fetch the maximum capacity from the database:
import requests
import db
def can_book_seats(
number_of_seats: int,
place_id: int,
) -> bool:
capacity = db.get_place_capacity(place_id) # sql query
booked = requests('https://partner.com/api').json()['booked'] # http req
return capacity >= number_of_seats + booked
Now testing this code will become a nightmare! It will require to setup:
real database and tables
fixture data
requests
mocks for different outcomes
and the whole Universe!
Our complexity has sky-rocketed!
And the most annoying part is that all other functions
that call can_book_seats
now also have to do the same setup.
It seams like IO
is indelible mark (some people also call it “effect”).
And at some point it time we will start to mix pure and impure code together. Let’s not forget that all of these operations can fail too!
Well, IO
mark is indeed indelible and should be respected.
And then impurity becomes explicit:
import requests
import db
from returns.io import IOResultE
def can_book_seats(
number_of_seats: int,
place_id: int,
) -> IOResultE[bool]:
...
Now this function returns IOResultE[bool]
instead of a regular bool
.
It means, that it cannot be used where regular bool
can be:
def process_booking_result(is_successful: bool) -> 'ProcessID':
...
is_successful: IOResultE[bool] = can_book_seats(number_of_seats, place_id)
process_booking_result(is_successful) # Boom!
# => Argument 1 has incompatible type "IOResultE[bool]"; expected "bool"
See? It is now impossible for a pure function to use IOResultE[bool]
.
It is impossible to unwrap or get a raw value from this container.
Once it is marked as IO
it will never return to the pure state
(well, there’s a hack actually:
unsafe_perform_io
).
Now we have to work inside the IO
context:
message_id: IOResultE['ProcessID'] = can_book_seats(
number_of_seats,
place_id,
).map(
process_booking_result,
)
Or it can be annotated to work with impure results:
def process_booking_result(
is_successful: IOResultE[bool],
) -> IOResultE['ProcessID']:
...
is_successful: IOResult[bool] = can_book_seats(number_of_seats, place_id)
process_booking_result(is_successful) # Works!
Now, all our impurity is explicit. We can track it, we can fight it, we can design it better. By saying that, it is assumed that you have a functional core and imperative shell.
You can also lift regular functions into one
that works with IO
or IOResult
on both ends.
It really helps you with the composition!
>>> from returns.io import IO
>>> from returns.pointfree import map_
>>> def regular_function(arg: int) -> float:
... return arg / 2 # not an `IO` operation
>>> container = IO(1)
>>> # When we need to compose `regular_function` with `IO`,
>>> # we have two ways of doing it:
>>> io = container.map(regular_function)
>>> assert io == IO(0.5)
>>> # or, it is the same as:
>>> io = map_(regular_function)(container)
>>> assert io == IO(0.5)
IOResult
can lift both regular functions and ones that return Result
:
>>> from returns.io import IOResult, IOSuccess
>>> from returns.pointfree import map_
>>> def regular_function(arg: int) -> float:
... return arg / 2 # not an `IO` operation
>>> container: IOResult[int, str] = IOSuccess(1)
>>> # When we need to compose `regular_function` with `IOResult`,
>>> # we have two ways of doing it:
>>> io = container.map(regular_function)
>>> assert io == IOSuccess(0.5)
>>> # or, it is the same as:
>>> io = map_(regular_function)(container)
>>> assert io == IOSuccess(0.5)
And Result
based functions:
>>> from returns.io import IOResult, IOSuccess
>>> from returns.result import Result, Success, Failure
>>> from returns.pointfree import bind_result
>>> def regular_function(arg: int) -> Result[float, str]:
... if arg > 0:
... return Success(arg / 2)
... return Failure('zero')
>>> assert bind_result(regular_function)(
... IOSuccess(1),
... ) == IOResult.from_result(regular_function(1))
Lifting is useful when using returns.pipeline.pipe()
and other different declarative tools.
IOResult
values can be matched using the new feature of Python 3.10,
Structural Pattern Matching,
see the example below:
from returns.io import IOFailure, IOResult, IOSuccess
from returns.result import Success
container: IOResult[int, str] = IOSuccess(42)
match container:
# Matches if the result stored inside `IOSuccess` is `42`
# We need to use `Success` until the custom matching protocol
# is released. For more information, please visit:
# https://www.python.org/dev/peps/pep-0622/#custom-matching-protocol
case IOSuccess(Success(42)):
print('Result is "42"')
# Matches any `IOSuccess` instance
# and binds its value to the `value` variable
case IOSuccess(value):
print('Result is "{0}"'.format(value))
# Matches any `IOFailure` instance
case IOFailure(_):
print('A failure was occurred')
There are several useful alises for IOResult
type with some common values:
returns.io.IOResultE
is an alias for IOResult[... Exception]
,
just use it when you want to work with IOResult
containers
that use exceptions as error type.
It is named IOResultE
because it is IOResultException
and IOResultError
at the same time.
Typing will only work correctly if our mypy plugin is used. This happens due to mypy issue.
We also have this handy decorator to help you with the existing impure things in Python:
from returns.io import impure
name: IO[str] = impure(input)('What is your name?')
You can also decorate your own functions
with @impure
for better readability and clearness:
import random
from returns.io import impure
@impure
def get_user() -> 'User':
return random.randint(1, 5)
Similar to impure
and safe
decorators.
Once applied, it transforms the return type to be IOResultE
:
from returns.io import IOResultE, impure_safe
@impure_safe
def http_get(path: str) -> 'Response':
return requests.get(path)
container: IOResultE['Response'] = http_get('/home')
Use for impure operations that might fail.
Don’t forget to check out Converters.
Sometimes you really need to get the raw value from IO
container.
For example:
def index_view(request, user_id):
user: IO[User] = get_user(user_id)
return render('index.html', {'user': user}) # ???
In this case your web-framework will not render your user correctly.
Since it does not expect it to be wrapped inside IO
containers.
And we obviously cannot map
or bind
this function.
What to do? Use unsafe_perform_io
:
from returns.unsafe import unsafe_perform_io
def index_view(request, user_id):
user: IO[User] = get_user(user_id)
return render('index.html', {'user': unsafe_perform_io(user)}) # Ok
We need it as an escape and compatibility mechanism for our imperative shell.
In other words:
>>> from returns.unsafe import unsafe_perform_io
>>> from returns.io import IO
>>> assert unsafe_perform_io(IO('abc')) == 'abc'
It is recommended
to use import-linter
to restrict imports from returns.unsafe
expect the top-level modules.
Inspired by Haskell’s unsafePerformIO
Please, note that our IO
implementation is not lazy by design.
This way when you mark something as @impure
it will work as previously.
The only thing that changes is the return type.
Instead we offer to use unsafe_perform_io
to work with IO
and simulate laziness.
But, you can always make your IO
lazy:
>>> from returns.io import IO
>>> lazy = lambda: IO(1)
>>> assert lazy() == IO(1)
We have decided that it would be better and more familiar for Python devs.
What kind of input parameter should
my function accept IO[T]
or simple T
?
It really depends on your domain / context.
If the value is pure, than use raw unwrapped values.
If the value is fetched, input, received, selected,
than use IO
or IOResult
container:
first one for operations that never fail,
second one for operations that might fail.
Most web applications are just fully covered with IO
.
We actually can! But, it is harder to write.
And IOResult
is actually the very same thing as IO[Result]
,
but has nicer API:
x: IO[Result[int, str]]
x.map(lambda io: io.map(lambda number: number + 1))
# Is the same as:
y: IOResult[int, str]
y.map(lambda number: number + 1)
The second one looks better, doesn’t it?
TLDR: you need to use IOSuccess
and IOFailure
functions
or IOResult.from_value
and IOResult.from_failure
methods:
>>> from returns.io import IOResult, IOSuccess, IOFailure
>>> first: IOResult[int, str] = IOSuccess(1)
>>> second: IOResult[float, int] = IOFailure(1)
>>> assert IOResult.from_value(1) == IOSuccess(1)
>>> assert IOResult.from_failure(2) == IOFailure(2)
You can also annotate your variables properly.
Otherwise, mypy
will treat IOSuccess(1)
as IOSuccess[int, Any]
.
You can narrow the type in advance.
See How to create unit objects? for more details.
Bases: BaseContainer
, SupportsKindN
[IO
, _ValueType
, NoReturn
, NoReturn
], IOLikeN
[_ValueType
, NoReturn
, NoReturn
]
Explicit container for impure function results.
We also sometimes call it “marker” since once it is marked, it cannot be ever unmarked. There’s no way to directly get its internal value.
Note that IO
represents a computation that never fails.
Examples of such computations are:
read / write to localStorage
get the current time
write to the console
get a random number
Use IOResult[...]
for operations that might fail.
Like DB access or network operations.
See also
inner_value (TypeVar
(_ValueType
, covariant=True)) –
Typesafe equality comparison with other Result objects.
Applies function to the inner value.
Applies ‘function’ to the contents of the IO instance and returns a new IO object containing the result. ‘function’ should accept a single “normal” (non-container) argument and return a non-container result.
>>> def mappable(string: str) -> str:
... return string + 'b'
>>> assert IO('a').map(mappable) == IO('ab')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)]) –
IO
[TypeVar
(_NewValueType
)]
Calls a wrapped function in a container on this container.
>>> from returns.io import IO
>>> assert IO('a').apply(IO(lambda inner: inner + 'b')) == IO('ab')
Or more complex example that shows how we can work
with regular functions and multiple IO
arguments:
>>> from returns.curry import curry
>>> @curry
... def appliable(first: str, second: str) -> str:
... return first + second
>>> assert IO('b').apply(IO('a').apply(IO(appliable))) == IO('ab')
Applies ‘function’ to the result of a previous calculation.
‘function’ should accept a single “normal” (non-container) argument
and return IO
type object.
>>> def bindable(string: str) -> IO[str]:
... return IO(string + 'b')
>>> assert IO('a').bind(bindable) == IO('ab')
Alias for bind method. Part of the IOLikeN interface.
Allows working with unwrapped values of containers in a safe way.
>>> from returns.io import IO
>>> assert IO.do(
... first + second
... for first in IO(2)
... for second in IO(3)
... ) == IO(5)
See Do Notation to learn more.
expr (Generator
[TypeVar
(_NewValueType
), None
, None
]) –
IO
[TypeVar
(_NewValueType
)]
Unit function to construct new IO
values.
Is the same as regular constructor:
>>> from returns.io import IO
>>> assert IO(1) == IO.from_value(1)
Part of the returns.interfaces.applicative.ApplicativeN
interface.
inner_value (TypeVar
(_NewValueType
)) –
IO
[TypeVar
(_NewValueType
)]
Unit function to construct new IO
values from existing IO
.
>>> from returns.io import IO
>>> assert IO(1) == IO.from_io(IO(1))
Part of the returns.interfaces.specific.IO.IOLikeN
interface.
Converts IOResult[a, b]
back to IO[Result[a, b]]
.
Can be really helpful for composition.
>>> from returns.io import IO, IOSuccess
>>> from returns.result import Success
>>> assert IO.from_ioresult(IOSuccess(1)) == IO(Success(1))
Is the reverse of returns.io.IOResult.from_typecast()
.
Decorator to mark function that it returns IO
container.
If you need to mark async
function as impure,
use returns.future.future()
instead.
This decorator only works with sync functions. Example:
>>> from returns.io import IO, impure
>>> @impure
... def function(arg: int) -> int:
... return arg + 1 # this action is pure, just an example
...
>>> assert function(1) == IO(2)
function (Callable
[[ParamSpec
(_FuncParams
)], TypeVar
(_NewValueType
)]) –
Callable
[[ParamSpec
(_FuncParams
)], IO
[TypeVar
(_NewValueType
)]]
Bases: BaseContainer
, SupportsKindN
[IOResult
, _ValueType
, _ErrorType
, NoReturn
], IOResultBasedN
[_ValueType
, _ErrorType
, NoReturn
]
Explicit container for impure function results that might fail.
Definition
This type is similar to returns.result.Result
.
This basically a more useful version of IO[Result[a, b]]
.
Use this type for IO
computations that might fail.
Examples of IO
computations that might fail are:
access database
access network
access filesystem
Use IO
for operations that do IO
but do not fail.
Note, that even methods like unwrap`()
and value_or()
return values wrapped in IO
.
IOResult
is a complex compound value that consists of:
raw value
Result
IO
This is why it has so many helper and factory methods:
You can construct IOResult
from raw values
with IOSuccess()
and IOFailure()
public type constructors
You can construct IOResult
from IO
values
with from_failed_io()
and IOResult.from_io()
You can construct IOResult
from Result
values
with from_result()
We also have a lot of utility methods for better function composition like:
bind_result()
to work
with functions which return Result
from_typecast()
to work with IO[Result[...]]
values
Implementation
This class contains all the methods that can be delegated to Result
.
But, some methods are not implemented which means
that we have to use special _IOSuccess
and _IOFailure
implementation details to correctly handle these callbacks.
Do not rely on them! Use public functions and types instead.
inner_value (Result
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True)]) –
Success type that is used to represent the successful computation.
inner_value (TypeVar
(_ValueType
, covariant=True)) –
alias of IOSuccess
Failure type that is used to represent the failed computation.
inner_value (TypeVar
(_ErrorType
, covariant=True)) –
alias of IOFailure
Typesafe equality comparison with other IOResult objects.
Returns a stack trace when IOFailure()
was called.
Swaps value and error types.
So, values become errors and errors become values.
It is useful when you have to work with errors a lot.
And since we have a lot of .bind_
related methods
and only a single .lash
- it is easier to work with values.
>>> from returns.io import IOSuccess, IOFailure
>>> assert IOSuccess(1).swap() == IOFailure(1)
>>> assert IOFailure(1).swap() == IOSuccess(1)
IOResult
[TypeVar
(_ErrorType
, covariant=True), TypeVar
(_ValueType
, covariant=True)]
Composes successful container with a pure function.
>>> from returns.io import IOSuccess
>>> assert IOSuccess(1).map(lambda num: num + 1) == IOSuccess(2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)]) –
IOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Calls a wrapped function in a container on this container.
>>> from returns.io import IOSuccess, IOFailure
>>> def appliable(first: str) -> str:
... return first + 'b'
>>> assert IOSuccess('a').apply(
... IOSuccess(appliable),
... ) == IOSuccess('ab')
>>> assert IOFailure('a').apply(
... IOSuccess(appliable),
... ) == IOFailure('a')
>>> assert IOSuccess('a').apply(IOFailure(1)) == IOFailure(1)
>>> assert IOFailure('a').apply(IOFailure('b')) == IOFailure('a')
Composes successful container with a function that returns a container.
>>> from returns.io import IOResult, IOFailure, IOSuccess
>>> def bindable(string: str) -> IOResult[str, str]:
... if len(string) > 1:
... return IOSuccess(string + 'b')
... return IOFailure(string + 'c')
>>> assert IOSuccess('aa').bind(bindable) == IOSuccess('aab')
>>> assert IOSuccess('a').bind(bindable) == IOFailure('ac')
>>> assert IOFailure('a').bind(bindable) == IOFailure('a')
Alias for bind_ioresult method. Part of the IOResultBasedN interface.
Composes successful container with a function that returns a container.
Similar to bind()
, but works with containers
that return returns.result.Result
instead of IOResult
.
>>> from returns.io import IOFailure, IOSuccess
>>> from returns.result import Result, Success
>>> def bindable(string: str) -> Result[str, str]:
... if len(string) > 1:
... return Success(string + 'b')
... return Failure(string + 'c')
>>> assert IOSuccess('aa').bind_result(bindable) == IOSuccess('aab')
>>> assert IOSuccess('a').bind_result(bindable) == IOFailure('ac')
>>> assert IOFailure('a').bind_result(bindable) == IOFailure('a')
Composes successful container with a function that returns a container.
Similar to bind()
, but works with containers
that return returns.io.IO
instead of IOResult
.
>>> from returns.io import IO, IOFailure, IOSuccess
>>> def bindable(string: str) -> IO[str]:
... return IO(string + 'z')
>>> assert IOSuccess('a').bind_io(bindable) == IOSuccess('az')
>>> assert IOFailure('a').bind_io(bindable) == IOFailure('a')
Composes failed container with a pure function to modify failure.
>>> from returns.io import IOFailure
>>> assert IOFailure(1).alt(float) == IOFailure(1.0)
function (Callable
[[TypeVar
(_ErrorType
, covariant=True)], TypeVar
(_NewErrorType
)]) –
IOResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
)]
Composes failed container with a function that returns a container.
>>> from returns.io import IOFailure, IOSuccess, IOResult
>>> def lashable(state: str) -> IOResult[int, str]:
... if len(state) > 1:
... return IOSuccess(len(state))
... return IOFailure('oops')
>>> assert IOFailure('a').lash(lashable) == IOFailure('oops')
>>> assert IOFailure('abc').lash(lashable) == IOSuccess(3)
>>> assert IOSuccess('a').lash(lashable) == IOSuccess('a')
Get value from successful container or default value from failed one.
>>> from returns.io import IO, IOFailure, IOSuccess
>>> assert IOSuccess(1).value_or(None) == IO(1)
>>> assert IOFailure(1).value_or(None) == IO(None)
default_value (TypeVar
(_NewValueType
)) –
IO
[Union
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewValueType
)]]
Get value from successful container or raise exception for failed one.
>>> from returns.io import IO, IOFailure, IOSuccess
>>> assert IOSuccess(1).unwrap() == IO(1)
>>> IOFailure(1).unwrap()
Traceback (most recent call last):
...
returns.primitives.exceptions.UnwrapFailedError
IO
[TypeVar
(_ValueType
, covariant=True)]
Get failed value from failed container or raise exception from success.
>>> from returns.io import IO, IOFailure, IOSuccess
>>> assert IOFailure(1).failure() == IO(1)
>>> IOSuccess(1).failure()
Traceback (most recent call last):
...
returns.primitives.exceptions.UnwrapFailedError
IO
[TypeVar
(_ErrorType
, covariant=True)]
Composes inner Result
with IOResult
returning function.
Can be useful when you need an access to both states of the result.
>>> from returns.io import IOResult, IOSuccess, IOFailure
>>> from returns.result import Result
>>> def count(container: Result[int, int]) -> IOResult[int, int]:
... return IOResult.from_result(
... container.map(lambda x: x + 1).alt(abs),
... )
>>> assert IOSuccess(1).compose_result(count) == IOSuccess(2)
>>> assert IOFailure(-1).compose_result(count) == IOFailure(1)
Allows working with unwrapped values of containers in a safe way.
>>> from returns.io import IOResult, IOFailure, IOSuccess
>>> assert IOResult.do(
... first + second
... for first in IOSuccess(2)
... for second in IOSuccess(3)
... ) == IOSuccess(5)
>>> assert IOResult.do(
... first + second
... for first in IOFailure('a')
... for second in IOSuccess(3)
... ) == IOFailure('a')
See Do Notation to learn more. This feature requires our mypy plugin.
expr (Generator
[TypeVar
(_NewValueType
), None
, None
]) –
IOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]
Converts IO[Result[_ValueType, _ErrorType]]
to IOResult
.
Also prevails the type of Result
to IOResult
, so:
IO[Result[_ValueType, _ErrorType]]
would become
IOResult[_ValueType, _ErrorType]
.
>>> from returns.result import Success
>>> from returns.io import IO, IOResult, IOSuccess
>>> container = IO(Success(1))
>>> assert IOResult.from_typecast(container) == IOSuccess(1)
Can be reverted via returns.io.IO.from_ioresult()
method.
Creates new IOResult
from “failed” IO
container.
>>> from returns.io import IO, IOResult, IOFailure
>>> container = IO(1)
>>> assert IOResult.from_failed_io(container) == IOFailure(1)
Creates new IOResult
from “successful” IO
container.
>>> from returns.io import IO, IOResult, IOSuccess
>>> container = IO(1)
>>> assert IOResult.from_io(container) == IOSuccess(1)
Creates IOResult
from Result
value.
>>> from returns.io import IOResult, IOSuccess, IOFailure
>>> from returns.result import Success, Failure
>>> assert IOResult.from_result(Success(1)) == IOSuccess(1)
>>> assert IOResult.from_result(Failure(2)) == IOFailure(2)
Creates IOResult
from existing IOResult
value.
>>> from returns.io import IOResult, IOSuccess, IOFailure
>>> assert IOResult.from_ioresult(IOSuccess(1)) == IOSuccess(1)
>>> assert IOResult.from_ioresult(IOFailure(2)) == IOFailure(2)
One more value to create success unit values.
It is useful as a united way to create a new value from any container.
>>> from returns.io import IOResult, IOSuccess
>>> assert IOResult.from_value(1) == IOSuccess(1)
You can use this method or IOSuccess()
,
choose the most convenient for you.
inner_value (TypeVar
(_NewValueType
)) –
IOResult
[TypeVar
(_NewValueType
), Any
]
One more value to create failure unit values.
It is useful as a united way to create a new value from any container.
>>> from returns.io import IOResult, IOFailure
>>> assert IOResult.from_failure(1) == IOFailure(1)
You can use this method or IOFailure()
,
choose the most convenient for you.
inner_value (TypeVar
(_NewErrorType
)) –
IOResult
[Any
, TypeVar
(_NewErrorType
)]
Bases: IOResult
[Any
, _ErrorType
]
IOFailure
representation.
inner_value (TypeVar
(_ErrorType
, covariant=True)) –
Alias for bind_ioresult method. Part of the IOResultBasedN interface. # noqa: E501
Bases: IOResult
[_ValueType
, Any
]
IOSuccess
representation.
inner_value (TypeVar
(_ValueType
, covariant=True)) –
Alias for bind_ioresult method. Part of the IOResultBasedN interface. # noqa: E501
Alias for a popular case when IOResult
has Exception
as error type.
alias of IOResult
[_ValueType
, Exception
]
Decorator to mark function that it returns IOResult
container.
Should be used with care, since it only catches Exception
subclasses.
It does not catch BaseException
subclasses.
If you need to mark async
function as impure,
use returns.future.future_safe()
instead.
This decorator only works with sync functions. Example:
>>> from returns.io import IOSuccess, impure_safe
>>> @impure_safe
... def function(arg: int) -> float:
... return 1 / arg
...
>>> assert function(1) == IOSuccess(1.0)
>>> assert function(0).failure()
Similar to returns.future.future_safe()
and returns.result.safe()
decorators.
function (Callable
[[ParamSpec
(_FuncParams
)], TypeVar
(_NewValueType
)]) –
Callable
[[ParamSpec
(_FuncParams
)], IOResult
[TypeVar
(_NewValueType
), Exception
]]
Compatibility utility and escape mechanism from IO
world.
Just unwraps the internal value
from returns.io.IO
container.
Should be used with caution!
Since it might be overused by lazy and ignorant developers.
It is recommended to have only one place (module / file) in your program where you allow unsafe operations.
We recommend to use import-linter
to enforce this rule.
>>> from returns.io import IO
>>> assert unsafe_perform_io(IO(1)) == 1
wrapped_in_io (IO
[TypeVar
(_ValueType
)]) –
TypeVar
(_ValueType
)
A set of primitives to work with async
functions.
Can be used with asyncio
, trio
, and curio
.
And any event-loop!
Tested with anyio.
What problems do we solve with these containers? Basically these ones:
You cannot call async function from a sync one
Any unexpectedly thrown exception can ruin your whole event loop
Ugly composition with lots of await statements
Without Future
container it is impossible to compose two functions:
sync and async one.
You simply cannot await
coroutines inside a sync context.
It is even a SyntaxError
.
def test():
await some()
# SyntaxError: 'await' outside async function
So, you have to turn you function into async one. And all callers of this function in async functions. And all their callers.
This is really hard to model. When you code has two types of uncomposable functions you increase your mental complexity by extreme levels.
Instead, you can use Future
container,
it allows you to model async interactions in a sync manner:
>>> from returns.future import Future
>>> async def first() -> int:
... return 1
>>> async def second(arg: int) -> int:
... return arg + 1
>>> def main() -> Future[int]: # sync function!
... return Future(first()).bind_awaitable(second)
Now we can compose async functions and maintaining a sync context!
It is also possible run a Future
with regular tools like asyncio.run
or anyio.run
:
>>> import anyio
>>> from returns.io import IO
>>> assert anyio.run(main().awaitable) == IO(2)
One more very useful thing Future
does behind the scenes is converting
its result to IO
-based containers.
This helps a lot when separating pure and impure
(async functions are impure) code inside your app.
This container becomes very useful when working
with async
function that can fail.
It works the similar way regular Result
does.
And is literally a wrapper around Future[Result[_V, _E]]
type.
Let’s see how it can be used in a real program:
1import asyncio # we use `asyncio` only as an example, you can use any io lib
2from typing import Sequence, cast
3
4import httpx # you would need to `pip install httpx`
5from typing_extensions import Final, TypedDict
6
7from returns.future import FutureResultE, future_safe
8from returns.io import IOResultE
9from returns.iterables import Fold
10
11_URL: Final = 'https://jsonplaceholder.typicode.com/posts/{0}'
12_Post = TypedDict('_Post', {
13 'id': int,
14 'userId': int,
15 'title': str,
16 'body': str,
17})
18
19
20@future_safe
21async def _fetch_post(post_id: int) -> _Post:
22 # Ideally, we can use `ReaderFutureResult` to provide `client` from deps.
23 async with httpx.AsyncClient(timeout=5) as client:
24 response = await client.get(_URL.format(post_id))
25 response.raise_for_status()
26 return cast(_Post, response.json()) # or validate the response
27
28
29def _show_titles(number_of_posts: int) -> Sequence[FutureResultE[str]]:
30 def factory(post: _Post) -> str:
31 return post['title']
32
33 return [
34 # Notice how easily we compose async and sync functions:
35 _fetch_post(post_id).map(factory)
36 # TODO: try `for post_id in (2, 1, 0):` to see how async errors work
37 for post_id in range(1, number_of_posts + 1)
38 ]
39
40
41async def main() -> IOResultE[Sequence[str]]:
42 """
43 Main entrypoint for the async world.
44
45 Let's fetch 3 titles of posts asynchronously.
46 We use `gather` to run requests in "parallel".
47 """
48 futures: Sequence[IOResultE[str]] = await asyncio.gather(*_show_titles(3))
49 return Fold.collect(futures, IOResultE.from_value(()))
50
51
52if __name__ == '__main__':
53 print(asyncio.run(main())) # noqa: WPS421
54 # <IOResult: <Success: (
55 # 'sunt aut facere repellat provident occaecati ...',
56 # 'qui est esse',
57 # 'ea molestias quasi exercitationem repellat qui ipsa sit aut',
58 # )>>
What is different?
We can now easily make show_titles
sync,
we can also make _fetch_post
sync,
but we would need to use ReaderFutureResult
container
with proper dependencies in this case
We now don’t care about errors at all. In this example any error will cancel the whole pipeline
We now have .map
method to easily compose sync and async functions
You can see the next example
with RequiresContextFutureResult
and without a single async/await
.
That example illustrates the whole point of our actions: writing
sync code that executes asynchronously without any magic at all.
There are several useful alises for FutureResult
type
with some common values:
returns.future.FutureResultE
is an alias
for FutureResult[... Exception]
,
just use it when you want to work with FutureResult
containers
that use exceptions as error type.
It is named FutureResultE
because it is FutureResultException
and FutureResultError
at the same time.
This decorator helps to easily transform async def
into Future
:
>>> import anyio
>>> from returns.future import future, Future
>>> from returns.io import IO
>>> @future
... async def test(arg: int) -> float:
... return arg / 2
>>> future_instance = test(1)
>>> assert isinstance(future_instance, Future)
>>> assert anyio.run(future_instance.awaitable) == IO(0.5)
Make sure that you decorate with @future
only coroutines
that do not throw exceptions. For ones that do, use future_safe
.
This decorator converts async def
into FutureResult
,
which means that it becomes:
Full featured Future
like container
Safe from any exceptions
Let’s dig into it:
>>> import anyio
>>> from returns.future import future_safe, FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> @future_safe
... async def test(arg: int) -> float:
... return 1 / arg
>>> future_instance = test(2)
>>> assert isinstance(future_instance, FutureResult)
>>> assert anyio.run(future_instance.awaitable) == IOSuccess(0.5)
>>> str(anyio.run(test(0).awaitable)) # this will fail
'<IOResult: <Failure: division by zero>>'
Never miss exceptions ever again!
Helper decorator to transform regular sync function into async
ones.
>>> import anyio
>>> from inspect import iscoroutinefunction
>>> from returns.future import asyncify
>>> @asyncify
... def your_function(x: int) -> int:
... return x + 1
>>> assert iscoroutinefunction(your_function) is True
>>> assert anyio.run(your_function, 1) == 2
Very important node: making your function async
does not mean
it will work asynchronously. It can still block if it uses blocking calls.
Here’s an example of how you must not do:
import requests
from returns.future import asyncify
@asyncify
def please_do_not_do_that():
return requests.get('...') # this will still block!
Do not overuse this decorator.
It is only useful for some basic composition
with Future
and FutureResult
.
For Future
container:
from_value
when you have a raw value
from_io
when you have existing IO
container
from_future_result
when you have existing FutureResult
For FutureResult
container:
from_value
when you want to mark some raw value as a Success
from_failure
when you want to mark some raw value as a Failure
from_result
when you already have Result
container
from_io
when you have successful IO
from_failed_io
when you have failed IO
from_future
when you have successful Future
from_failed_future
when you have failed Future
from_typecast
when you have existing Future[Result]
There’s almost none.
The only difference is that FutureResult[a, b]
is a handy wrapper
around Future[Result[a, b]]
,
so you won’t need to use methods like .map
and .bind
twice.
You can always convert it with methods like
.from_typecast
and .from_future_result
.
Async function that returns its argument.
>>> import anyio
>>> from returns.future import async_identity
>>> assert anyio.run(async_identity, 1) == 1
See returns.functions.identity()
for sync version of this function and more docs and examples.
instance (TypeVar
(_FirstType
)) –
TypeVar
(_FirstType
)
Bases: BaseContainer
, SupportsKindN
[Future
, _ValueType
, NoReturn
, NoReturn
], FutureBasedN
[_ValueType
, NoReturn
, NoReturn
]
Container to easily compose async
functions.
Represents a better abstraction over a simple coroutine.
Is framework, event-loop, and IO-library agnostics.
Works with asyncio
, curio
, trio
, or any other tool.
Internally we use anyio
to test
that it works as expected for any io stack.
Note that Future[a]
represents a computation
that never fails and returns IO[a]
type.
Use FutureResult[a, b]
for operations that might fail.
Like DB access or network operations.
Is not related to asyncio.Future
in any kind.
Tradeoffs
Due to possible performance issues we move all coroutines definitions to a separate module.
See also
inner_value (Awaitable
[TypeVar
(_ValueType
, covariant=True)]) –
Transforms Future[a]
to Awaitable[IO[a]]
.
Use this method when you need a real coroutine.
Like for asyncio.run
calls.
Note, that returned value will be wrapped
in returns.io.IO
container.
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> assert anyio.run(Future.from_value(1).awaitable) == IO(1)
IO
[TypeVar
(_ValueType
, covariant=True)]
Applies function to the inner value.
Applies ‘function’ to the contents of the IO instance
and returns a new Future
object containing the result.
‘function’ should accept a single “normal” (non-container) argument
and return a non-container result.
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> def mappable(x: int) -> int:
... return x + 1
>>> assert anyio.run(
... Future.from_value(1).map(mappable).awaitable,
... ) == IO(2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)]) –
Future
[TypeVar
(_NewValueType
)]
Calls a wrapped function in a container on this container.
>>> import anyio
>>> from returns.future import Future
>>> def transform(arg: int) -> str:
... return str(arg) + 'b'
>>> assert anyio.run(
... Future.from_value(1).apply(
... Future.from_value(transform),
... ).awaitable,
... ) == IO('1b')
Applies ‘function’ to the result of a previous calculation.
‘function’ should accept a single “normal” (non-container) argument
and return Future
type object.
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> def bindable(x: int) -> Future[int]:
... return Future.from_value(x + 1)
>>> assert anyio.run(
... Future.from_value(1).bind(bindable).awaitable,
... ) == IO(2)
Alias for bind method. Part of the FutureBasedN interface.
Compose a container and async
function returning a container.
This function should return a container value.
See bind_awaitable()
to bind async
function that returns a plain value.
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> async def coroutine(x: int) -> Future[str]:
... return Future.from_value(str(x + 1))
>>> assert anyio.run(
... Future.from_value(1).bind_async(coroutine).awaitable,
... ) == IO('2')
Alias for bind_async method. Part of the FutureBasedN interface.
Allows to compose a container and a regular async
function.
This function should return plain, non-container value.
See bind_async()
to bind async
function that returns a container.
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> async def coroutine(x: int) -> int:
... return x + 1
>>> assert anyio.run(
... Future.from_value(1).bind_awaitable(coroutine).awaitable,
... ) == IO(2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Awaitable
[TypeVar
(_NewValueType
)]]) –
Future
[TypeVar
(_NewValueType
)]
Applies ‘function’ to the result of a previous calculation.
‘function’ should accept a single “normal” (non-container) argument
and return IO
type object.
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> def bindable(x: int) -> IO[int]:
... return IO(x + 1)
>>> assert anyio.run(
... Future.from_value(1).bind_io(bindable).awaitable,
... ) == IO(2)
Allows working with unwrapped values of containers in a safe way.
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> async def main() -> bool:
... return await Future.do(
... first + second
... async for first in Future.from_value(2)
... async for second in Future.from_value(3)
... ) == IO(5)
>>> assert anyio.run(main) is True
See Do Notation to learn more.
expr (AsyncGenerator
[TypeVar
(_NewValueType
), None
]) –
Future
[TypeVar
(_NewValueType
)]
Allows to create a Future
from a plain value.
The resulting Future
will just return the given value
wrapped in returns.io.IO
container when awaited.
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> async def main() -> bool:
... return (await Future.from_value(1)) == IO(1)
>>> assert anyio.run(main) is True
inner_value (TypeVar
(_NewValueType
)) –
Future
[TypeVar
(_NewValueType
)]
Creates a new Future
from the existing one.
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> future = Future.from_value(1)
>>> assert anyio.run(Future.from_future(future).awaitable) == IO(1)
Part of the FutureBasedN
interface.
Allows to create a Future
from IO
container.
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> async def main() -> bool:
... return (await Future.from_io(IO(1))) == IO(1)
>>> assert anyio.run(main) is True
Creates Future[Result[a, b]]
instance from FutureResult[a, b]
.
This method is the inverse of from_typecast()
.
>>> import anyio
>>> from returns.future import Future, FutureResult
>>> from returns.io import IO
>>> from returns.result import Success
>>> container = Future.from_future_result(FutureResult.from_value(1))
>>> assert anyio.run(container.awaitable) == IO(Success(1))
inner_value (FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]) –
Future
[Result
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]]
Decorator to turn a coroutine definition into Future
container.
>>> import anyio
>>> from returns.io import IO
>>> from returns.future import future
>>> @future
... async def test(x: int) -> int:
... return x + 1
>>> assert anyio.run(test(1).awaitable) == IO(2)
function (Callable
[[ParamSpec
(_FuncParams
)], Coroutine
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ValueType
, covariant=True)]]) –
Callable
[[ParamSpec
(_FuncParams
)], Future
[TypeVar
(_ValueType
, covariant=True)]]
Decorator to turn a common function into an asynchronous function.
This decorator is useful for composition with Future
and
FutureResult
containers.
Warning
This function will not your sync function run like async one. It will still be a blocking function that looks like async one. We recommend to only use this decorator with functions that do not access network or filesystem. It is only a composition helper, not a transformer.
Usage example:
>>> import anyio
>>> from returns.future import asyncify
>>> @asyncify
... def test(x: int) -> int:
... return x + 1
>>> assert anyio.run(test, 1) == 2
Read more about async and sync functions: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
function (Callable
[[ParamSpec
(_FuncParams
)], TypeVar
(_ValueType
, covariant=True)]) –
Callable
[[ParamSpec
(_FuncParams
)], Coroutine
[Any
, Any
, TypeVar
(_ValueType
, covariant=True)]]
Bases: BaseContainer
, SupportsKindN
[FutureResult
, _ValueType
, _ErrorType
, NoReturn
], FutureResultBasedN
[_ValueType
, _ErrorType
, NoReturn
]
Container to easily compose async
functions.
Represents a better abstraction over a simple coroutine.
Is framework, event-loop, and IO-library agnostics.
Works with asyncio
, curio
, trio
, or any other tool.
Internally we use anyio
to test
that it works as expected for any io stack.
Note that FutureResult[a, b]
represents a computation
that can fail and returns IOResult[a, b]
type.
Use Future[a]
for operations that cannot fail.
This is a Future
that returns Result
type.
By providing this utility type we make developers’ lives easier.
FutureResult
has a lot of composition helpers
to turn complex nested operations into a one function calls.
Tradeoffs
Due to possible performance issues we move all coroutines definitions to a separate module.
See also
inner_value (Awaitable
[Result
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True)]]) –
Transforms FutureResult[a, b]
to Awaitable[IOResult[a, b]]
.
Use this method when you need a real coroutine.
Like for asyncio.run
calls.
Note, that returned value will be wrapped
in returns.io.IOResult
container.
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess
>>> assert anyio.run(
... FutureResult.from_value(1).awaitable,
... ) == IOSuccess(1)
IOResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True)]
Swaps value and error types.
So, values become errors and errors become values.
It is useful when you have to work with errors a lot.
And since we have a lot of .bind_
related methods
and only a single .lash
.
It is easier to work with values than with errors.
>>> import anyio
>>> from returns.future import FutureSuccess, FutureFailure
>>> from returns.io import IOSuccess, IOFailure
>>> assert anyio.run(FutureSuccess(1).swap) == IOFailure(1)
>>> assert anyio.run(FutureFailure(1).swap) == IOSuccess(1)
FutureResult
[TypeVar
(_ErrorType
, covariant=True), TypeVar
(_ValueType
, covariant=True)]
Applies function to the inner value.
Applies ‘function’ to the contents of the IO instance
and returns a new FutureResult
object containing the result.
‘function’ should accept a single “normal” (non-container) argument
and return a non-container result.
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> def mappable(x: int) -> int:
... return x + 1
>>> assert anyio.run(
... FutureResult.from_value(1).map(mappable).awaitable,
... ) == IOSuccess(2)
>>> assert anyio.run(
... FutureResult.from_failure(1).map(mappable).awaitable,
... ) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Calls a wrapped function in a container on this container.
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> def appliable(x: int) -> int:
... return x + 1
>>> assert anyio.run(
... FutureResult.from_value(1).apply(
... FutureResult.from_value(appliable),
... ).awaitable,
... ) == IOSuccess(2)
>>> assert anyio.run(
... FutureResult.from_failure(1).apply(
... FutureResult.from_value(appliable),
... ).awaitable,
... ) == IOFailure(1)
>>> assert anyio.run(
... FutureResult.from_value(1).apply(
... FutureResult.from_failure(2),
... ).awaitable,
... ) == IOFailure(2)
>>> assert anyio.run(
... FutureResult.from_failure(1).apply(
... FutureResult.from_failure(2),
... ).awaitable,
... ) == IOFailure(1)
container (KindN
[FutureResult
, Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)], TypeVar
(_ErrorType
, covariant=True), Any
]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Applies ‘function’ to the result of a previous calculation.
‘function’ should accept a single “normal” (non-container) argument
and return Future
type object.
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> def bindable(x: int) -> FutureResult[int, str]:
... return FutureResult.from_value(x + 1)
>>> assert anyio.run(
... FutureResult.from_value(1).bind(bindable).awaitable,
... ) == IOSuccess(2)
>>> assert anyio.run(
... FutureResult.from_failure(1).bind(bindable).awaitable,
... ) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], KindN
[FutureResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), Any
]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Alias for bind method. Part of the FutureResultBasedN interface.
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], KindN
[FutureResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), Any
]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Composes a container and async
function returning container.
This function should return a container value.
See bind_awaitable()
to bind async
function that returns a plain value.
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> async def coroutine(x: int) -> FutureResult[str, int]:
... return FutureResult.from_value(str(x + 1))
>>> assert anyio.run(
... FutureResult.from_value(1).bind_async(coroutine).awaitable,
... ) == IOSuccess('2')
>>> assert anyio.run(
... FutureResult.from_failure(1).bind_async(coroutine).awaitable,
... ) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Awaitable
[KindN
[FutureResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), Any
]]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Alias for bind_async method. Part of the FutureResultBasedN interface.
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Awaitable
[KindN
[FutureResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), Any
]]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Allows to compose a container and a regular async
function.
This function should return plain, non-container value.
See bind_async()
to bind async
function that returns a container.
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> async def coro(x: int) -> int:
... return x + 1
>>> assert anyio.run(
... FutureResult.from_value(1).bind_awaitable(coro).awaitable,
... ) == IOSuccess(2)
>>> assert anyio.run(
... FutureResult.from_failure(1).bind_awaitable(coro).awaitable,
... ) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Awaitable
[TypeVar
(_NewValueType
)]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Binds a function returning Result[a, b]
container.
>>> import anyio
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.result import Result, Success
>>> from returns.future import FutureResult
>>> def bind(inner_value: int) -> Result[int, str]:
... return Success(inner_value + 1)
>>> assert anyio.run(
... FutureResult.from_value(1).bind_result(bind).awaitable,
... ) == IOSuccess(2)
>>> assert anyio.run(
... FutureResult.from_failure('a').bind_result(bind).awaitable,
... ) == IOFailure('a')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Result
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Binds a function returning IOResult[a, b]
container.
>>> import anyio
>>> from returns.io import IOResult, IOSuccess, IOFailure
>>> from returns.future import FutureResult
>>> def bind(inner_value: int) -> IOResult[int, str]:
... return IOSuccess(inner_value + 1)
>>> assert anyio.run(
... FutureResult.from_value(1).bind_ioresult(bind).awaitable,
... ) == IOSuccess(2)
>>> assert anyio.run(
... FutureResult.from_failure('a').bind_ioresult(bind).awaitable,
... ) == IOFailure('a')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], IOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Binds a function returning IO[a]
container.
>>> import anyio
>>> from returns.io import IO, IOSuccess, IOFailure
>>> from returns.future import FutureResult
>>> def bind(inner_value: int) -> IO[float]:
... return IO(inner_value + 0.5)
>>> assert anyio.run(
... FutureResult.from_value(1).bind_io(bind).awaitable,
... ) == IOSuccess(1.5)
>>> assert anyio.run(
... FutureResult.from_failure(1).bind_io(bind).awaitable,
... ) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], IO
[TypeVar
(_NewValueType
)]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Binds a function returning Future[a]
container.
>>> import anyio
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.future import Future, FutureResult
>>> def bind(inner_value: int) -> Future[float]:
... return Future.from_value(inner_value + 0.5)
>>> assert anyio.run(
... FutureResult.from_value(1).bind_future(bind).awaitable,
... ) == IOSuccess(1.5)
>>> assert anyio.run(
... FutureResult.from_failure(1).bind_future(bind).awaitable,
... ) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Future
[TypeVar
(_NewValueType
)]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Composes a container and async
function returning Future
.
Similar to bind_future()
but works with async functions.
>>> import anyio
>>> from returns.future import Future, FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> async def coroutine(x: int) -> Future[str]:
... return Future.from_value(str(x + 1))
>>> assert anyio.run(
... FutureResult.from_value(1).bind_async_future,
... coroutine,
... ) == IOSuccess('2')
>>> assert anyio.run(
... FutureResult.from_failure(1).bind_async,
... coroutine,
... ) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Awaitable
[Future
[TypeVar
(_NewValueType
)]]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Composes failed container with a pure function to modify failure.
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> def altable(arg: int) -> int:
... return arg + 1
>>> assert anyio.run(
... FutureResult.from_value(1).alt(altable).awaitable,
... ) == IOSuccess(1)
>>> assert anyio.run(
... FutureResult.from_failure(1).alt(altable).awaitable,
... ) == IOFailure(2)
function (Callable
[[TypeVar
(_ErrorType
, covariant=True)], TypeVar
(_NewErrorType
)]) –
FutureResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
)]
Composes failed container with a function that returns a container.
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess
>>> def lashable(x: int) -> FutureResult[int, str]:
... return FutureResult.from_value(x + 1)
>>> assert anyio.run(
... FutureResult.from_value(1).lash(lashable).awaitable,
... ) == IOSuccess(1)
>>> assert anyio.run(
... FutureResult.from_failure(1).lash(lashable).awaitable,
... ) == IOSuccess(2)
function (Callable
[[TypeVar
(_ErrorType
, covariant=True)], KindN
[FutureResult
, TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
), Any
]]) –
FutureResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
)]
Composes inner Result
with FutureResult
returning function.
Can be useful when you need an access to both states of the result.
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.result import Result
>>> def count(container: Result[int, int]) -> FutureResult[int, int]:
... return FutureResult.from_result(
... container.map(lambda x: x + 1).alt(abs),
... )
>>> assert anyio.run(
... FutureResult.from_value(1).compose_result, count,
... ) == IOSuccess(2)
>>> assert anyio.run(
... FutureResult.from_failure(-1).compose_result, count,
... ) == IOFailure(1)
function (Callable
[[Result
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True)]], KindN
[FutureResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), Any
]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]
Allows working with unwrapped values of containers in a safe way.
>>> import anyio
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> async def success() -> bool:
... return await FutureResult.do(
... first + second
... async for first in FutureResult.from_value(2)
... async for second in FutureResult.from_value(3)
... ) == IOSuccess(5)
>>> assert anyio.run(success) is True
>>> async def failure() -> bool:
... return await FutureResult.do(
... first + second
... async for first in FutureResult.from_value(2)
... async for second in FutureResult.from_failure(3)
... ) == IOFailure(3)
>>> assert anyio.run(failure) is True
See Do Notation to learn more.
expr (AsyncGenerator
[TypeVar
(_NewValueType
), None
]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]
Creates FutureResult[a, b]
from Future[Result[a, b]]
.
>>> import anyio
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.result import Success, Failure
>>> from returns.future import Future, FutureResult
>>> async def main():
... assert await FutureResult.from_typecast(
... Future.from_value(Success(1)),
... ) == IOSuccess(1)
... assert await FutureResult.from_typecast(
... Future.from_value(Failure(1)),
... ) == IOFailure(1)
>>> anyio.run(main)
inner_value (Future
[Result
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]
Creates FutureResult
from successful Future
value.
>>> import anyio
>>> from returns.io import IOSuccess
>>> from returns.future import Future, FutureResult
>>> async def main():
... assert await FutureResult.from_future(
... Future.from_value(1),
... ) == IOSuccess(1)
>>> anyio.run(main)
inner_value (Future
[TypeVar
(_NewValueType
)]) –
FutureResult
[TypeVar
(_NewValueType
), Any
]
Creates FutureResult
from failed Future
value.
>>> import anyio
>>> from returns.io import IOFailure
>>> from returns.future import Future, FutureResult
>>> async def main():
... assert await FutureResult.from_failed_future(
... Future.from_value(1),
... ) == IOFailure(1)
>>> anyio.run(main)
inner_value (Future
[TypeVar
(_NewErrorType
)]) –
FutureResult
[Any
, TypeVar
(_NewErrorType
)]
Creates new FutureResult
from existing one.
>>> import anyio
>>> from returns.io import IOSuccess
>>> from returns.future import FutureResult
>>> async def main():
... assert await FutureResult.from_future_result(
... FutureResult.from_value(1),
... ) == IOSuccess(1)
>>> anyio.run(main)
Part of the FutureResultLikeN
interface.
inner_value (FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]
Creates FutureResult
from successful IO
value.
>>> import anyio
>>> from returns.io import IO, IOSuccess
>>> from returns.future import FutureResult
>>> async def main():
... assert await FutureResult.from_io(
... IO(1),
... ) == IOSuccess(1)
>>> anyio.run(main)
inner_value (IO
[TypeVar
(_NewValueType
)]) –
FutureResult
[TypeVar
(_NewValueType
), Any
]
Creates FutureResult
from failed IO
value.
>>> import anyio
>>> from returns.io import IO, IOFailure
>>> from returns.future import FutureResult
>>> async def main():
... assert await FutureResult.from_failed_io(
... IO(1),
... ) == IOFailure(1)
>>> anyio.run(main)
inner_value (IO
[TypeVar
(_NewErrorType
)]) –
FutureResult
[Any
, TypeVar
(_NewErrorType
)]
Creates FutureResult
from IOResult
value.
>>> import anyio
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.future import FutureResult
>>> async def main():
... assert await FutureResult.from_ioresult(
... IOSuccess(1),
... ) == IOSuccess(1)
... assert await FutureResult.from_ioresult(
... IOFailure(1),
... ) == IOFailure(1)
>>> anyio.run(main)
inner_value (IOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]
Creates FutureResult
from Result
value.
>>> import anyio
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.result import Success, Failure
>>> from returns.future import FutureResult
>>> async def main():
... assert await FutureResult.from_result(
... Success(1),
... ) == IOSuccess(1)
... assert await FutureResult.from_result(
... Failure(1),
... ) == IOFailure(1)
>>> anyio.run(main)
inner_value (Result
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]) –
FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]
Creates FutureResult
from successful value.
>>> import anyio
>>> from returns.io import IOSuccess
>>> from returns.future import FutureResult
>>> async def main():
... assert await FutureResult.from_value(
... 1,
... ) == IOSuccess(1)
>>> anyio.run(main)
inner_value (TypeVar
(_NewValueType
)) –
FutureResult
[TypeVar
(_NewValueType
), Any
]
Creates FutureResult
from failed value.
>>> import anyio
>>> from returns.io import IOFailure
>>> from returns.future import FutureResult
>>> async def main():
... assert await FutureResult.from_failure(
... 1,
... ) == IOFailure(1)
>>> anyio.run(main)
inner_value (TypeVar
(_NewErrorType
)) –
FutureResult
[Any
, TypeVar
(_NewErrorType
)]
Public unit function to create successful FutureResult
objects.
Is the same as from_value()
.
>>> import anyio
>>> from returns.future import FutureResult, FutureSuccess
>>> assert anyio.run(FutureSuccess(1).awaitable) == anyio.run(
... FutureResult.from_value(1).awaitable,
... )
inner_value (TypeVar
(_NewValueType
)) –
FutureResult
[TypeVar
(_NewValueType
), Any
]
Public unit function to create failed FutureResult
objects.
Is the same as from_failure()
.
>>> import anyio
>>> from returns.future import FutureResult, FutureFailure
>>> assert anyio.run(FutureFailure(1).awaitable) == anyio.run(
... FutureResult.from_failure(1).awaitable,
... )
inner_value (TypeVar
(_NewErrorType
)) –
FutureResult
[Any
, TypeVar
(_NewErrorType
)]
Alias for a popular case when Result
has Exception
as error type.
alias of FutureResult
[_ValueType
, Exception
]
Decorator to convert exception-throwing coroutine to FutureResult
.
Should be used with care, since it only catches Exception
subclasses.
It does not catch BaseException
subclasses.
If you need to mark sync function as safe
,
use returns.future.future_safe()
instead.
This decorator only works with async
functions. Example:
>>> import anyio
>>> from returns.future import future_safe
>>> from returns.io import IOSuccess, IOResult
>>> @future_safe
... async def might_raise(arg: int) -> float:
... return 1 / arg
...
>>> assert anyio.run(might_raise(2).awaitable) == IOSuccess(0.5)
>>> assert isinstance(
... anyio.run(might_raise(0).awaitable),
... IOResult.failure_type,
... )
Similar to returns.io.impure_safe()
and returns.result.safe()
decorators, but works with async
functions.
function (Callable
[[ParamSpec
(_FuncParams
)], Coroutine
[TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ValueType
, covariant=True)]]) –
Callable
[[ParamSpec
(_FuncParams
)], FutureResult
[TypeVar
(_ValueType
, covariant=True), Exception
]]
Dependency injection is a popular software architecture pattern.
It’s main idea is that you provide Inversion of Control and can pass different things into your logic instead of hardcoding you stuff. And by doing this you are on your way to achieve Single Responsibility for your functions and objects.
A lot of programs we write rely on the context implicitly or explicitly. We can rely on configuration, env variables, stubs, logical dependencies, etc.
Let’s look at the example.
One of the most popular errors Python developers do in Django
is that they overuse settings
object inside the business logic.
This makes your logic framework-oriented
and hard to reason about in large projects.
Because values just pop out of nowhere in a deeply nested functions. And can be changed from the outside, from the context of your app.
Imagine that you have a django
based game,
where you award users with points
for each guessed letter in a word (unguessed letters are marked as '.'
):
from django.http import HttpRequest, HttpResponse
from words_app.logic import calculate_points
def view(request: HttpRequest) -> HttpResponse:
user_word: str = request.POST['word'] # just an example
points = calculate_points(user_word)
... # later you show the result to user somehow
# Somewhere in your `words_app/logic.py`:
def calculate_points(word: str) -> int:
guessed_letters_count = len([letter for letter in word if letter != '.'])
return _award_points_for_letters(guessed_letters_count)
def _award_points_for_letters(guessed: int) -> int:
return 0 if guessed < 5 else guessed # minimum 6 points possible!
Straight and simple!
But, later you decide to make the game more fun: let’s make the minimal accountable letters threshold configurable for an extra challenge.
You can just do it directly:
def _award_points_for_letters(guessed: int, threshold: int) -> int:
return 0 if guessed < threshold else guessed
And now your code won’t simply type-check. Because that’s how our caller looks like:
def calculate_points(word: str) -> int:
guessed_letters_count = len([letter for letter in word if letter != '.'])
return _award_points_for_letters(guessed_letters_count)
To fix this calculate_points
function
(and all other upper caller functions)
will have to accept threshold: int
as a parameter and pass it to _award_points_for_letters
.
Imagine that your large project has multiple things to configure in multiple functions. What a mess it would be!
Ok, you can directly use django.settings
(or similar)
in your _award_points_for_letters
function.
And ruin your pure logic with framework-specific details. That’s ugly!
We have learned that this tiny change showed us that it is not so easy to rely on implicit app context.
And instead of passing parameters for all callstack
or using dirty framework specific magic
you can use RequiresContext
container.
That was built just for this case.
Let’s see how our code changes:
from django.conf import settings
from django.http import HttpRequest, HttpResponse
from words_app.logic import calculate_points
def view(request: HttpRequest) -> HttpResponse:
user_word: str = request.POST['word'] # just an example
points = calculate_points(user_words)(settings) # passing the dependencies
... # later you show the result to user somehow
# Somewhere in your `words_app/logic.py`:
from typing_extensions import Protocol
from returns.context import RequiresContext
class _Deps(Protocol): # we rely on abstractions, not direct values or types
WORD_THRESHOLD: int
def calculate_points(word: str) -> RequiresContext[int, _Deps]:
guessed_letters_count = len([letter for letter in word if letter != '.'])
return _award_points_for_letters(guessed_letters_count)
def _award_points_for_letters(guessed: int) -> RequiresContext[int, _Deps]:
return RequiresContext(
lambda deps: 0 if guessed < deps.WORD_THRESHOLD else guessed,
)
And now you can pass your dependencies in a really direct and explicit way.
Let’s try to configure how we mark our unguessed letters
(previously unguessed letters were marked as '.'
).
Let’s say, we want to change this to be _
.
How can we do that with our existing function?
def calculate_points(word: str) -> RequiresContext[int, _Deps]:
guessed_letters_count = len([letter for letter in word if letter != '.'])
return _award_points_for_letters(guessed_letters_count)
We are already using RequiresContext
,
but its dependencies are just hidden from us!
We have a special helper for this case: .ask()
,
which returns us current dependencies.
The only thing we need to is to properly
annotate the type for our case: RequiresContext[int, _Deps].ask()
Sadly, currently mypy
is not able to infer the dependency type
out of the context and we need to explicitly provide it.
Let’s see the final result:
from returns.context import RequiresContext
class _Deps(Protocol): # we rely on abstractions, not direct values or types
WORD_THRESHOLD: int
UNGUESSED_CHAR: str
def calculate_points(word: str) -> RequiresContext[int, _Deps]:
def factory(deps: _Deps) -> RequiresContext[int, _Deps]:
guessed_letters_count = len([
letter for letter in word if letter != deps.UNGUESSED_CHAR
])
return _award_points_for_letters(guessed_letters_count)
return RequiresContext[int, _Deps].ask().bind(factory)
And now we access the current context from any place in our callstack. Isn’t it convenient?
Warning
RequiresContext
and similar types are not recursion safe.
If you would have nesting of more than sys.getrecursionlimit()
you will end up with RecursionError
.
Will this ever happen to you? Probably not.
The concept behind
RequiresContext
container is really simple.
It is a container around Callable[[EnvType], ReturnType]
function.
By its definition it works with pure functions that never fails.
It can be illustrated as a simple nested function:
>>> from typing import Callable
>>> def first(limit: int) -> Callable[[str], bool]:
... def inner(deps: str) -> bool:
... return len(deps) > limit
... return inner
>>> assert first(2)('abc') # first(limit)(deps)
>>> assert not first(5)('abc') # first(limit)(deps)
That’s basically enough to make dependency injection possible.
But how would you compose first
function?
Let’s say with the following function:
>>> def bool_to_str(arg: bool) -> str:
... return 'ok' if arg else 'nope'
It would be hard, knowing that it returns another function to be called later when the context is known.
We can wrap it in RequiresContext
container to allow better composition!
>>> from returns.context import RequiresContext
>>> def first(limit: int) -> RequiresContext[bool, str]:
... def inner(deps: str) -> bool:
... return len(deps) > limit
... return RequiresContext(inner) # wrapping function here!
>>> assert first(1).map(bool_to_str)('abc') == 'ok'
>>> assert first(5).map(bool_to_str)('abc') == 'nope'
There’s how execution flows:
RequiresContext execution flow.¶
The rule is: the dependencies are injected at the very last moment in time. And then normal logical execution happens.
RequiresContextResult
container
is a combination of RequiresContext[Result[a, b], env]
.
Which means that it is a wrapper around pure function that might fail.
We also added a lot of useful methods for this container, so you can work easily with it:
bind_result()
allows to bind functions that return Result
with just one call
bind_context()
allows to bind functions that return RequiresContext
easily
There are also several useful constructors from any possible type
Use it when you work with pure context-related functions that might fail.
RequiresContextIOResult
container
is a combination of RequiresContext[IOResult[a, b], env]
.
Which means that it is a wrapper around impure function that might fail.
We also added a lot of useful methods for this container, so you can work easily with it:
bind_result()
allows to bind functions that return Result
with just one call
bind_io()
allows to bind functions that return IO
with just one call
bind_ioresult()
allows to bind functions that return IOResult
with just one call
bind_context()
allows to bind functions that return RequiresContext
easily
bind_context_result()
allows to bind functions that return RequiresContextResult
easily
There are also several useful constructors from any possible type
Use it when you work with impure context-related functions that might fail. This is basically the main type that is going to be used in most apps.
RequiresContextFutureResult
container
is a combination of RequiresContext[FutureResult[a, b], env]
.
Which means that it is a wrapper around impure async function that might fail.
Here’s how it should be used:
1from typing import Sequence, cast
2
3import anyio # you would need to `pip install anyio`
4import httpx # you would need to `pip install httpx`
5from typing_extensions import Final, TypedDict
6
7from returns.context import RequiresContextFutureResultE
8from returns.functions import tap
9from returns.future import FutureResultE, future_safe
10from returns.iterables import Fold
11from returns.pipeline import managed
12from returns.result import ResultE, safe
13
14_URL: Final = 'https://jsonplaceholder.typicode.com/posts/{0}'
15_Post = TypedDict('_Post', {
16 'id': int,
17 'userId': int,
18 'title': str,
19 'body': str,
20})
21
22
23def _close(
24 client: httpx.AsyncClient,
25 raw_value: ResultE[Sequence[str]],
26) -> FutureResultE[None]:
27 return future_safe(client.aclose)()
28
29
30def _fetch_post(
31 post_id: int,
32) -> RequiresContextFutureResultE[_Post, httpx.AsyncClient]:
33 context: RequiresContextFutureResultE[
34 httpx.AsyncClient,
35 httpx.AsyncClient,
36 ] = RequiresContextFutureResultE.ask()
37
38 return context.bind_future_result(
39 lambda client: future_safe(client.get)(_URL.format(post_id)),
40 ).bind_result(
41 safe(tap(httpx.Response.raise_for_status)),
42 ).map(
43 lambda response: cast(_Post, response.json()), # or validate it
44 )
45
46
47def _show_titles(
48 number_of_posts: int,
49) -> RequiresContextFutureResultE[Sequence[str], httpx.AsyncClient]:
50 def factory(post: _Post) -> str:
51 return post['title']
52
53 titles = [
54 # Notice how easily we compose async and sync functions:
55 _fetch_post(post_id).map(factory)
56 # TODO: try `for post_id in (2, 1, 0):` to see how errors work
57 for post_id in range(1, number_of_posts + 1)
58 ]
59 return Fold.collect(titles, RequiresContextFutureResultE.from_value(()))
60
61
62if __name__ == '__main__':
63 # Let's fetch 3 titles of posts one-by-one, but with async client,
64 # because we want to highlight `managed` in this example:
65 managed_httpx = managed(_show_titles(3), _close)
66 future_result = managed_httpx(
67 FutureResultE.from_value(httpx.AsyncClient(timeout=5)),
68 )
69 print(anyio.run(future_result.awaitable)) # noqa: WPS421
70 # <IOResult: <Success: (
71 # 'sunt aut facere repellat provident occaecati ...',
72 # 'qui est esse',
73 # 'ea molestias quasi exercitationem repellat qui ipsa sit aut',
74 # )>>
This example illustrates the whole point of our actions: writing sync code that executes asynchronously without any magic at all!
We also added a lot of useful methods for this container, so you can work easily with it.
These methods are identical with RequiresContextIOResult
:
bind_result()
allows to bind functions that return Result
with just one call
bind_io()
allows to bind functions that return IO
with just one call
bind_ioresult()
allows to bind functions that return IOResult
with just one call
bind_future_result()
allows to bind functions that return FutureResult
with just one call
bind_context()
allows to bind functions that return RequiresContext
easily
bind_context_result()
allows to bind functions that return RequiresContextResult
easily
There are new ones:
bind_future()
allows to bind functions that return Future
container
bind_future_result()
allows to bind functions that return FutureResult
container
bind_async_future()
allows to bind async functions that return Future
container
bind_async_future_result()
allows to bind async functions that return FutureResult
container
bind_context_ioresult()
allows to bind functions that return RequiresContextIOResult
bind_async()
allows to bind async functions
that return RequiresContextFutureResult
container
bind_awaitable()
allows to bind async function that return raw values
Use it when you work with impure context-related functions that might fail. This is basically the main type that is going to be used in most apps.
There are several useful aliases for RequiresContext
and friends with some common values:
Reader
is an alias for RequiresContext[...]
to save you some typing.
Uses Reader
because it is a native name for this concept from Haskell.
RequiresContextResultE
is an alias for RequiresContextResult[..., Exception]
,
just use it when you want to work with RequiresContextResult
containers
that use exceptions as error type.
It is named ResultE
because it is ResultException
and ResultError
at the same time.
ReaderResult
is an alias for RequiresContextResult[...]
to save you some typing.
ReaderResultE
is an alias for RequiresContextResult[..., Exception]
RequiresContextIOResultE
is an alias for RequiresContextIOResult[..., Exception]
ReaderIOResult
is an alias for RequiresContextIOResult[...]
to save you some typing.
ReaderIOResultE
is an alias for RequiresContextIOResult[..., Exception]
RequiresContextFutureResultE
is an alias for RequiresContextFutureResult[..., Exception]
ReaderFutureResult
is an alias for RequiresContextFutureResult[...]
to save you some typing.
ReaderFutureResultE
is an alias for RequiresContextFutureResult[..., Exception]
RequiresContext
requires you to use one of the following methods:
from_value
when you have a raw value
from_requires_context_result
when you have RequiresContextResult
from_requires_context_ioresult
when you have RequiresContextIOResult
RequiresContextResult
requires you to use one of the following methods:
from_value
when you want to mark some raw value as a Success
from_failure
when you want to mark some raw value as a Failure
from_result
when you already have Result
container
from_context
when you have successful RequiresContext
from_failed_context
when you have failed RequiresContext
from_typecast
when you have RequiresContext[..., Result]
RequiresContextIOResult
requires you to use one of the following methods:
from_value
when you want to mark some raw value as a Success
from_failure
when you want to mark some raw value as a Failure
from_result
when you already have Result
container
from_io
when you have successful IO
container
from_failed_io
when you have failed IO
container
from_ioresult
when you already have IOResult
container
from_context
when you have successful RequiresContext
container
from_failed_context
when you have failed RequiresContext
container
from_result_context
when you have RequiresContextResult
container
from_typecast
when you have RequiresContext[..., IOResult]
RequiresContextFutureResult
requires
you to use one of the following methods:
from_value
when you want to mark some raw value as a Success
from_failure
when you want to mark some raw value as a Failure
from_result
when you already have Result
container
from_io
when you have successful IO
container
from_failed_io
when you have failed IO
container
from_ioresult
when you already have IOResult
container
from_future
when you already have successful Future
container
from_failed_future
when you already have failed Future
container
from_future_result
when you already have FutureResult
container
from_context
when you have successful RequiresContext
from_failed_context
when you have failed RequiresContext
from_result_context
when you have RequiresContextResult
container
from_ioresult_context
when you have RequiresContextIOResult
container
from_typecast
when you have RequiresContext[..., IOResult]
Use .ask()
method!
See this guide.
Yes, this container might remind a traditional decorator with arguments, let see an example:
>>> def example(print_result: bool):
... def decorator(function):
... def factory(*args, **kwargs):
... original = function(*args, **kwargs)
... if print_result:
... print(original)
... return original
... return factory
... return decorator
And it can be used like so:
>>> @example(print_result=True)
... def my_function(first: int, second: int) -> int:
... return first + second
>>> assert my_function(2, 3) == 5
5
We can model the similar idea with RequiresContext
:
>>> from returns.context import RequiresContext
>>> def my_function(first: int, second: int) -> RequiresContext[int, bool]:
... def factory(print_result: bool) -> int:
... original = first + second
... if print_result:
... print(original)
... return original
... return RequiresContext(factory)
>>> assert my_function(2, 3)(False) == 5
>>> assert my_function(2, 3)(True) == 5
5
As you can see,
it is easier to change the behaviour of a function with RequiresContext
.
While decorator with arguments glues values to a function forever.
Decide when you need which behaviour carefully.
We actually can! But, it is harder to write.
And RequiresContextResult
is actually
the very same thing as RequiresContext[Result, e]
, but has nicer API:
x: RequiresContext[Result[int, str], int]
x.map(lambda result: result.map(lambda number: number + 1))
# Is the same as:
y: RequiresContextResult[int, str, int]
y.map(lambda number: number + 1)
The second one looks better, doesn’t it?
The same applies for RequiresContextIOResult
and RequiresContextFutureResult
as well.
Because mypy
cannot possibly know the type of current context.
This is hard even for a plugin.
So, using this technique is better:
from returns.context import RequiresContext
def some_context(*args, **kwargs) -> RequiresContext[str, int]:
def factory(deps: int) -> RequiresContext[str, int]:
...
return RequiresContext[str, int].ask().bind(factory)
Dependency Injection pattern and
Inversion of Control
principle forms a lot of ideas and tooling
that do pretty much the same as RequiresContext
container.
What is the difference? Why do we need each of them?
Let’s find out! Tools like dependencies or punq tries to:
Inspect (by name or type respectively) function or class that needs dependencies
Build the required dependency tree from the source defined in the service container
There are other tools like inject
that also invades
your code with @inject
decorator.
RequiresContext
works completely different.
It respects your code and does not try to inspect in any manner.
It also does not care about building dependencies at all.
All it does is: provides simple API to compose functions that need additional context (or dependencies) to run.
You can even use them together: RequiresContext
will pass dependencies
built by punq
(or any other tool of your choice)
as a deps
parameter to RequiresContext
instance.
When to use which? Let’s dig into it!
RequiresContext
offers explicit context passing
for the whole function stack inside your program.
This means two things: you will have to pass it through all your code and
use it everywhere inside your program explicitly,
when you need to access the environment and dependencies
Traditional DI
allows to leave a lot
of code unaware of dependency injection.
Because you don’t have to maintain the context everywhere.
You just need to adjust your API to meet the dependency injector requirements.
On the other hand, you lose explicitness here.
So when to use RequiresContext
?
When you write pure functional code
When you want to know which code relies on context and which is free from it,
RequiresContext
makes this explicit and typed
When you rely on types inside your program
When you want to rely on functions rather than magic
When not to use RequiresContext
and use traditional DI?
When you already have a lot of code written in a different approach: in OOP and/or imperative styles
When you need to pass dependencies into a very deep level of your call stack implicitly (without modifying the whole stack), this is called magic
When you not rely on types for dependencies. There are cases when DI is made by names or tags
Here’s an example that might give you a better understanding of how
RequiresContext
is used on real and rather big projects:
from typing import Callable, Dict, Protocol, final
from returns.io import IOResultE
from returns.context import ReaderIOResultE
class _SyncPermissionsDeps(Protocol):
fetch_metadata: Callable[[], IOResultE['Metadata']]
get_user_permissions: Callable[['Metadata'], Dict[int, str]] # pure
update_bi_permissions: Callable[[Dict[int, str]], IOResultE['Payload']]
def sync_permissions() -> ReaderIOResultE[_SyncPermissionsDeps, 'Payload']:
"""
This functions runs a scheduled task once a day.
It syncs permissions from the metadata storage to our BI system.
"""
def factory(deps: _SyncPermissionsDeps) -> IOResultE['Payload']:
return deps.fetch_metadata().map(
deps.get_user_permissions,
).bind_ioresult(
deps.update_bi_permissions,
)
return ReaderIOResult(factory)
And then it is called like so:
# tasks.py
from celery import shared_task
from returns.functions import raise_exception
from logic.usecases.sync_permissions import sync_permissions
from infrastructure.implemented import Container
from infrastructure.services import bi
from infrastructure.repositories import db
@shared_task(autoretry_for=(ConnectionError,), max_retries=3)
def queue_sync_permissions():
# Building the container with dependencies to pass it into the context.
# We also make sure that we don't forget to raise internal exceptions
# and trigger celery retries.
return sync_permissions().alt(raise_exception)(Container(
fetch_metadata=db.select_user_metadata,
get_user_permissions=bi.permissions_from_user,
update_bi_permissions=bi.put_user_permissions,
))
Sometimes RequiresContext
and other similar types might be used with
no explicit dependencies so we need to have this type alias for Any.
Bases: BaseContainer
, SupportsKindN
[RequiresContext
, _ReturnType
, _EnvType
, NoReturn
], ReaderBased2
[_ReturnType
, _EnvType
]
The RequiresContext
container.
It’s main purpose is to wrap some specific function and to provide tools to compose other functions around it without actually calling it.
The RequiresContext
container passes the state
you want to share between functions.
Functions may read that state, but can’t change it.
The RequiresContext
container
lets us access shared immutable state within a specific context.
It can be used for lazy evaluation and typed dependency injection.
RequiresContext
is used with functions that never fail.
If you want to use RequiresContext
with returns Result
then consider using RequiresContextResult
instead.
Note
This container does not wrap ANY value. It wraps only functions. You won’t be able to supply arbitrary types!
See also
inner_value (Callable
[[TypeVar
(_EnvType
, contravariant=True)], TypeVar
(_ReturnType
, covariant=True)]) –
A convenient placeholder to call methods created by .from_value():
Allows to compose functions inside the wrapped container.
Here’s how it works:
>>> from returns.context import RequiresContext
>>> def first(lg: bool) -> RequiresContext[int, float]:
... # `deps` has `float` type here:
... return RequiresContext(
... lambda deps: deps if lg else -deps,
... )
>>> assert first(True).map(lambda number: number * 10)(2.5) == 25.0
>>> assert first(False).map(lambda number: number * 10)(0.1) -1.0
function (Callable
[[TypeVar
(_ReturnType
, covariant=True)], TypeVar
(_NewReturnType
)]) –
RequiresContext
[TypeVar
(_NewReturnType
), TypeVar
(_EnvType
, contravariant=True)]
Calls a wrapped function in a container on this container.
>>> from returns.context import RequiresContext
>>> assert RequiresContext.from_value('a').apply(
... RequiresContext.from_value(lambda inner: inner + 'b')
... )(...) == 'ab'
container (KindN
[RequiresContext
, Callable
[[TypeVar
(_ReturnType
, covariant=True)], TypeVar
(_NewReturnType
)], TypeVar
(_EnvType
, contravariant=True), Any
]) –
RequiresContext
[TypeVar
(_NewReturnType
), TypeVar
(_EnvType
, contravariant=True)]
Composes a container with a function returning another container.
This is useful when you do several computations that rely on the same context.
>>> from returns.context import RequiresContext
>>> def first(lg: bool) -> RequiresContext[int, float]:
... # `deps` has `float` type here:
... return RequiresContext(
... lambda deps: deps if lg else -deps,
... )
>>> def second(number: int) -> RequiresContext[str, float]:
... # `deps` has `float` type here:
... return RequiresContext(
... lambda deps: '>=' if number >= deps else '<',
... )
>>> assert first(True).bind(second)(1) == '>='
>>> assert first(False).bind(second)(2) == '<'
function (Callable
[[TypeVar
(_ReturnType
, covariant=True)], KindN
[RequiresContext
, TypeVar
(_NewReturnType
), TypeVar
(_EnvType
, contravariant=True), Any
]]) –
RequiresContext
[TypeVar
(_NewReturnType
), TypeVar
(_EnvType
, contravariant=True)]
Alias for bind_context method, it is the same as bind here.
function (Callable
[[TypeVar
(_ReturnType
, covariant=True)], KindN
[RequiresContext
, TypeVar
(_NewReturnType
), TypeVar
(_EnvType
, contravariant=True), Any
]]) –
RequiresContext
[TypeVar
(_NewReturnType
), TypeVar
(_EnvType
, contravariant=True)]
Allows to modify the environment type.
>>> from returns.context import RequiresContext
>>> def mul(arg: int) -> RequiresContext[float, int]:
... return RequiresContext(lambda deps: arg * deps)
>>> assert mul(3).modify_env(int)('2') == 6
function (Callable
[[TypeVar
(_NewEnvType
)], TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContext
[TypeVar
(_ReturnType
, covariant=True), TypeVar
(_NewEnvType
)]
Get current context to use the dependencies.
It is a common scenario when you need to use the environment.
For example, you want to do some context-related computation,
but you don’t have the context instance at your disposal.
That’s where .ask()
becomes useful!
>>> from typing_extensions import TypedDict
>>> class Deps(TypedDict):
... message: str
>>> def first(lg: bool) -> RequiresContext[int, Deps]:
... # `deps` has `Deps` type here:
... return RequiresContext(
... lambda deps: deps['message'] if lg else 'error',
... )
>>> def second(text: str) -> RequiresContext[int, Deps]:
... return first(len(text) > 3)
>>> assert second('abc')({'message': 'ok'}) == 'error'
>>> assert second('abcd')({'message': 'ok'}) == 'ok'
And now imagine that you have to change this 3
limit.
And you want to be able to set it via environment as well.
Ok, let’s fix it with the power of RequiresContext.ask()
!
>>> from typing_extensions import TypedDict
>>> class Deps(TypedDict):
... message: str
... limit: int # note this new field!
>>> def new_first(lg: bool) -> RequiresContext[int, Deps]:
... # `deps` has `Deps` type here:
... return RequiresContext(
... lambda deps: deps['message'] if lg else 'err',
... )
>>> def new_second(text: str) -> RequiresContext[int, Deps]:
... return RequiresContext[int, Deps].ask().bind(
... lambda deps: new_first(len(text) > deps.get('limit', 3)),
... )
>>> assert new_second('abc')({'message': 'ok', 'limit': 2}) == 'ok'
>>> assert new_second('abcd')({'message': 'ok'}) == 'ok'
>>> assert new_second('abcd')({'message': 'ok', 'limit': 5}) == 'err'
That’s how ask
works.
This class contains methods that require to explicitly set type annotations. Why? Because it is impossible to figure out the type without them. So, here’s how you should use them:
RequiresContext[int, Dict[str, str]].ask()
Otherwise, your .ask()
method
will return RequiresContext[<nothing>, <nothing>]
,
which is unusable:
env = RequiresContext.ask()
env(some_deps)
And mypy
will warn you: error: Need type annotation for '...'
:rtype: RequiresContext
[TypeVar
(_EnvType
, contravariant=True), TypeVar
(_EnvType
, contravariant=True)]
Used to return some specific value from the container.
Consider this method as some kind of factory.
Passed value will be a return type.
Make sure to use no_args
for getting the unit value.
>>> from returns.context import RequiresContext
>>> unit = RequiresContext.from_value(5)
>>> assert unit(RequiresContext.no_args) == 5
Might be used with or without direct type hint.
inner_value (TypeVar
(_FirstType
)) –
RequiresContext
[TypeVar
(_FirstType
), Any
]
Used to create new containers from existing ones.
Used as a part of ReaderBased2
interface.
>>> from returns.context import RequiresContext
>>> unit = RequiresContext.from_value(5)
>>> assert RequiresContext.from_context(unit)(...) == unit(...)
inner_value (RequiresContext
[TypeVar
(_NewReturnType
), TypeVar
(_NewEnvType
)]) –
RequiresContext
[TypeVar
(_NewReturnType
), TypeVar
(_NewEnvType
)]
Typecasts RequiresContextResult
to RequiresContext
instance.
Breaks RequiresContextResult[a, b, e]
into RequiresContext[Result[a, b], e]
.
>>> from returns.context import RequiresContext
>>> from returns.context import RequiresContextResult
>>> from returns.result import Success
>>> assert RequiresContext.from_requires_context_result(
... RequiresContextResult.from_value(1),
... )(...) == Success(1)
Can be reverted with RequiresContextResult.from_typecast
.
inner_value (RequiresContextResult
[TypeVar
(_ValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContext
[Result
[TypeVar
(_ValueType
), TypeVar
(_ErrorType
)], TypeVar
(_EnvType
, contravariant=True)]
Typecasts RequiresContextIOResult
to RequiresContext
instance.
Breaks RequiresContextIOResult[a, b, e]
into RequiresContext[IOResult[a, b], e]
.
>>> from returns.context import RequiresContext
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess
>>> assert RequiresContext.from_requires_context_ioresult(
... RequiresContextIOResult.from_value(1),
... )(...) == IOSuccess(1)
Can be reverted with RequiresContextIOResult.from_typecast
.
inner_value (RequiresContextIOResult
[TypeVar
(_ValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContext
[IOResult
[TypeVar
(_ValueType
), TypeVar
(_ErrorType
)], TypeVar
(_EnvType
, contravariant=True)]
Typecasts RequiresContextIOResult
to RequiresContext
instance.
Breaks RequiresContextIOResult[a, b, e]
into RequiresContext[IOResult[a, b], e]
.
>>> import anyio
>>> from returns.context import RequiresContext
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOSuccess
>>> container = RequiresContext.from_requires_context_future_result(
... RequiresContextFutureResult.from_value(1),
... )
>>> assert anyio.run(
... container, RequiresContext.no_args,
... ) == IOSuccess(1)
Can be reverted with RequiresContextFutureResult.from_typecast
.
inner_value (RequiresContextFutureResult
[TypeVar
(_ValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContext
[FutureResult
[TypeVar
(_ValueType
), TypeVar
(_ErrorType
)], TypeVar
(_EnvType
, contravariant=True)]
Sometimes RequiresContext is too long to type.
inner_value (Callable
[[TypeVar
(_EnvType
, contravariant=True)], TypeVar
(_ReturnType
, covariant=True)]) –
Bases: BaseContainer
, SupportsKindN
[RequiresContextResult
, _ValueType
, _ErrorType
, _EnvType
], ReaderResultBasedN
[_ValueType
, _ErrorType
, _EnvType
]
The RequiresContextResult
combinator.
See returns.context.requires_context.RequiresContext
for more docs.
This is just a handy wrapper around RequiresContext[Result[a, b], env]
which represents a context-dependent pure operation
that might fail and return returns.result.Result
.
It has several important differences from the regular Result
classes.
It does not have Success
and Failure
subclasses.
Because, the computation is not yet performed.
And we cannot know the type in advance.
So, this is a thin wrapper, without any changes in logic.
Why do we need this wrapper? That’s just for better usability!
>>> from returns.context import RequiresContext
>>> from returns.result import Success, Result
>>> def function(arg: int) -> Result[int, str]:
... return Success(arg + 1)
>>> # Without wrapper:
>>> assert RequiresContext.from_value(Success(1)).map(
... lambda result: result.bind(function),
... )(...) == Success(2)
>>> # With wrapper:
>>> assert RequiresContextResult.from_value(1).bind_result(
... function,
... )(...) == Success(2)
This way RequiresContextResult
allows to simply work with:
raw values and pure functions
RequiresContext
values and pure functions returning it
Result
and functions returning it
Important implementation details
Due it is meaning, RequiresContextResult
cannot have Success
and Failure
subclasses.
We only have just one type. That’s by design.
Different converters are also not supported for this type.
Use converters inside the RequiresContext
context, not outside.
See also
inner_value (Callable
[[TypeVar
(_EnvType
, contravariant=True)], Result
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True)]]) –
A convenient placeholder to call methods created by .from_value().
Swaps value and error types.
So, values become errors and errors become values.
It is useful when you have to work with errors a lot.
And since we have a lot of .bind_
related methods
and only a single .lash
- it is easier to work with values.
>>> from returns.context import RequiresContextResult
>>> from returns.result import Failure, Success
>>> success = RequiresContextResult.from_value(1)
>>> failure = RequiresContextResult.from_failure(1)
>>> assert success.swap()(...) == Failure(1)
>>> assert failure.swap()(...) == Success(1)
RequiresContextResult
[TypeVar
(_ErrorType
, covariant=True), TypeVar
(_ValueType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Composes successful container with a pure function.
>>> from returns.context import RequiresContextResult
>>> from returns.result import Success, Failure
>>> assert RequiresContextResult.from_value(1).map(
... lambda x: x + 1,
... )(...) == Success(2)
>>> assert RequiresContextResult.from_failure(1).map(
... lambda x: x + 1,
... )(...) == Failure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)]) –
RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Calls a wrapped function in a container on this container.
>>> from returns.context import RequiresContextResult
>>> from returns.result import Success, Failure, Result
>>> def transform(arg: str) -> str:
... return arg + 'b'
>>> assert RequiresContextResult.from_value('a').apply(
... RequiresContextResult.from_value(transform),
... )(...) == Success('ab')
>>> assert RequiresContextResult.from_failure('a').apply(
... RequiresContextResult.from_value(transform),
... )(...) == Failure('a')
>>> assert isinstance(RequiresContextResult.from_value('a').apply(
... RequiresContextResult.from_failure(transform),
... )(...), Result.failure_type) is True
container (KindN
[RequiresContextResult
, Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)], TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Composes this container with a function returning the same type.
>>> from returns.context import RequiresContextResult
>>> from returns.result import Success, Failure
>>> def first(lg: bool) -> RequiresContextResult[int, int, float]:
... # `deps` has `float` type here:
... return RequiresContextResult(
... lambda deps: Success(deps) if lg else Failure(-deps),
... )
>>> def second(
... number: int,
... ) -> RequiresContextResult[str, int, float]:
... # `deps` has `float` type here:
... return RequiresContextResult(
... lambda deps: Success('>=' if number >= deps else '<'),
... )
>>> assert first(True).bind(second)(1) == Success('>=')
>>> assert first(False).bind(second)(2) == Failure(-2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], KindN
[RequiresContextResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Alias for bind_context_result method, it is the same as bind here.
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], KindN
[RequiresContextResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds Result
returning function to current container.
>>> from returns.context import RequiresContextResult
>>> from returns.result import Success, Failure, Result
>>> def function(num: int) -> Result[str, int]:
... return Success(num + 1) if num > 0 else Failure('<0')
>>> assert RequiresContextResult.from_value(1).bind_result(
... function,
... )(RequiresContextResult.no_args) == Success(2)
>>> assert RequiresContextResult.from_value(0).bind_result(
... function,
... )(RequiresContextResult.no_args) == Failure('<0')
>>> assert RequiresContextResult.from_failure(':(').bind_result(
... function,
... )(RequiresContextResult.no_args) == Failure(':(')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Result
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]]) –
RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds RequiresContext
returning function to current container.
>>> from returns.context import RequiresContext
>>> from returns.result import Success, Failure
>>> def function(arg: int) -> RequiresContext[int, str]:
... return RequiresContext(lambda deps: len(deps) + arg)
>>> assert function(2)('abc') == 5
>>> assert RequiresContextResult.from_value(2).bind_context(
... function,
... )('abc') == Success(5)
>>> assert RequiresContextResult.from_failure(2).bind_context(
... function,
... )('abc') == Failure(2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], RequiresContext
[TypeVar
(_NewValueType
), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Composes failed container with a pure function.
>>> from returns.context import RequiresContextResult
>>> from returns.result import Success, Failure
>>> assert RequiresContextResult.from_value(1).alt(
... lambda x: x + 1,
... )(...) == Success(1)
>>> assert RequiresContextResult.from_failure(1).alt(
... lambda x: x + 1,
... )(...) == Failure(2)
function (Callable
[[TypeVar
(_ErrorType
, covariant=True)], TypeVar
(_NewErrorType
)]) –
RequiresContextResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Composes this container with a function returning the same type.
>>> from returns.context import RequiresContextResult
>>> from returns.result import Success, Failure
>>> def lashable(arg: str) -> RequiresContextResult[str, str, str]:
... if len(arg) > 1:
... return RequiresContextResult(
... lambda deps: Success(deps + arg),
... )
... return RequiresContextResult(
... lambda deps: Failure(arg + deps),
... )
>>> assert RequiresContextResult.from_value('a').lash(
... lashable,
... )('c') == Success('a')
>>> assert RequiresContextResult.from_failure('a').lash(
... lashable,
... )('c') == Failure('ac')
>>> assert RequiresContextResult.from_failure('aa').lash(
... lashable,
... )('b') == Success('baa')
function (Callable
[[TypeVar
(_ErrorType
, covariant=True)], KindN
[RequiresContextResult
, TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Allows to modify the environment type.
>>> from returns.context import RequiresContextResultE
>>> from returns.result import Success, safe
>>> def div(arg: int) -> RequiresContextResultE[float, int]:
... return RequiresContextResultE(
... safe(lambda deps: arg / deps),
... )
>>> assert div(3).modify_env(int)('2') == Success(1.5)
>>> assert div(3).modify_env(int)('0').failure()
function (Callable
[[TypeVar
(_NewEnvType
)], TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContextResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_NewEnvType
)]
Is used to get the current dependencies inside the call stack.
Similar to returns.context.requires_context.RequiresContext.ask()
,
but returns Result
instead of a regular value.
Please, refer to the docs there to learn how to use it.
One important note that is worth duplicating here:
you might need to provide _EnvType
explicitly,
so mypy
will know about it statically.
>>> from returns.context import RequiresContextResultE
>>> from returns.result import Success
>>> assert RequiresContextResultE[int, int].ask().map(
... str,
... )(1) == Success('1')
RequiresContextResult
[TypeVar
(_EnvType
, contravariant=True), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Creates new container with Result
as a unit value.
>>> from returns.context import RequiresContextResult
>>> from returns.result import Success, Failure
>>> deps = RequiresContextResult.no_args
>>> assert RequiresContextResult.from_result(
... Success(1),
... )(deps) == Success(1)
>>> assert RequiresContextResult.from_result(
... Failure(1),
... )(deps) == Failure(1)
inner_value (Result
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]) –
RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), Any
]
You might end up with RequiresContext[Result[...]]
as a value.
This method is designed to turn it into RequiresContextResult
.
It will save all the typing information.
It is just more useful!
>>> from returns.context import RequiresContext
>>> from returns.result import Success, Failure
>>> assert RequiresContextResult.from_typecast(
... RequiresContext.from_value(Success(1)),
... )(RequiresContextResult.no_args) == Success(1)
>>> assert RequiresContextResult.from_typecast(
... RequiresContext.from_value(Failure(1)),
... )(RequiresContextResult.no_args) == Failure(1)
inner_value (RequiresContext
[Result
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)], TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Creates new container from RequiresContext
as a success unit.
>>> from returns.context import RequiresContext
>>> from returns.result import Success
>>> assert RequiresContextResult.from_context(
... RequiresContext.from_value(1),
... )(...) == Success(1)
inner_value (RequiresContext
[TypeVar
(_NewValueType
), TypeVar
(_NewEnvType
)]) –
RequiresContextResult
[TypeVar
(_NewValueType
), Any
, TypeVar
(_NewEnvType
)]
Creates new container from RequiresContext
as a failure unit.
>>> from returns.context import RequiresContext
>>> from returns.result import Failure
>>> assert RequiresContextResult.from_failed_context(
... RequiresContext.from_value(1),
... )(...) == Failure(1)
inner_value (RequiresContext
[TypeVar
(_NewValueType
), TypeVar
(_NewEnvType
)]) –
RequiresContextResult
[Any
, TypeVar
(_NewValueType
), TypeVar
(_NewEnvType
)]
Creates RequiresContextResult
from another instance of it.
>>> from returns.context import ReaderResult
>>> from returns.result import Success, Failure
>>> assert ReaderResult.from_result_context(
... ReaderResult.from_value(1),
... )(...) == Success(1)
>>> assert ReaderResult.from_result_context(
... ReaderResult.from_failure(1),
... )(...) == Failure(1)
inner_value (RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]) –
RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]
Creates new container with Success(inner_value)
as a unit value.
>>> from returns.context import RequiresContextResult
>>> from returns.result import Success
>>> assert RequiresContextResult.from_value(1)(...) == Success(1)
inner_value (TypeVar
(_FirstType
)) –
RequiresContextResult
[TypeVar
(_FirstType
), Any
, Any
]
Creates new container with Failure(inner_value)
as a unit value.
>>> from returns.context import RequiresContextResult
>>> from returns.result import Failure
>>> assert RequiresContextResult.from_failure(1)(...) == Failure(1)
inner_value (TypeVar
(_FirstType
)) –
RequiresContextResult
[Any
, TypeVar
(_FirstType
), Any
]
Alias for a popular case when Result
has Exception
as error type.
alias of RequiresContextResult
[_ValueType
, Exception
, _EnvType
]
Alias to save you some typing. Uses original name from Haskell.
inner_value (Callable
[[TypeVar
(_EnvType
, contravariant=True)], Result
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True)]]) –
Alias to save you some typing. Has Exception
as error type.
alias of RequiresContextResult
[_ValueType
, Exception
, _EnvType
]
Bases: BaseContainer
, SupportsKindN
[RequiresContextIOResult
, _ValueType
, _ErrorType
, _EnvType
], ReaderIOResultBasedN
[_ValueType
, _ErrorType
, _EnvType
]
The RequiresContextIOResult
combinator.
See returns.context.requires_context.RequiresContext
and returns.context.requires_context_result.RequiresContextResult
for more docs.
This is just a handy wrapper around
RequiresContext[IOResult[a, b], env]
which represents a context-dependent impure operation that might fail.
It has several important differences from the regular Result
classes.
It does not have Success
and Failure
subclasses.
Because, the computation is not yet performed.
And we cannot know the type in advance.
So, this is a thin wrapper, without any changes in logic.
Why do we need this wrapper? That’s just for better usability!
>>> from returns.context import RequiresContext
>>> from returns.io import IOSuccess, IOResult
>>> def function(arg: int) -> IOResult[int, str]:
... return IOSuccess(arg + 1)
>>> # Without wrapper:
>>> assert RequiresContext.from_value(IOSuccess(1)).map(
... lambda ioresult: ioresult.bind(function),
... )(...) == IOSuccess(2)
>>> # With wrapper:
>>> assert RequiresContextIOResult.from_value(1).bind_ioresult(
... function,
... )(...) == IOSuccess(2)
This way RequiresContextIOResult
allows to simply work with:
raw values and pure functions
RequiresContext
values and pure functions returning it
RequiresContextResult
values and pure functions returning it
Result
and pure functions returning it
IOResult
and functions returning it
other RequiresContextIOResult
related functions and values
This is a complex type for complex tasks!
Important implementation details
Due it is meaning, RequiresContextIOResult
cannot have Success
and Failure
subclasses.
We only have just one type. That’s by design.
Different converters are also not supported for this type.
Use converters inside the RequiresContext
context, not outside.
See also
inner_value (Callable
[[TypeVar
(_EnvType
, contravariant=True)], IOResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
)]]) –
A convenient placeholder to call methods created by .from_value().
Swaps value and error types.
So, values become errors and errors become values.
It is useful when you have to work with errors a lot.
And since we have a lot of .bind_
related methods
and only a single .lash
- it is easier to work with values.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess, IOFailure
>>> success = RequiresContextIOResult.from_value(1)
>>> failure = RequiresContextIOResult.from_failure(1)
>>> assert success.swap()(...) == IOFailure(1)
>>> assert failure.swap()(...) == IOSuccess(1)
RequiresContextIOResult
[TypeVar
(_ErrorType
), TypeVar
(_ValueType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Composes successful container with a pure function.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert RequiresContextIOResult.from_value(1).map(
... lambda x: x + 1,
... )(...) == IOSuccess(2)
>>> assert RequiresContextIOResult.from_failure(1).map(
... lambda x: x + 1,
... )(...) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Calls a wrapped function in a container on this container.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess, IOFailure
>>> def transform(arg: str) -> str:
... return arg + 'b'
>>> assert RequiresContextIOResult.from_value('a').apply(
... RequiresContextIOResult.from_value(transform),
... )(...) == IOSuccess('ab')
>>> assert RequiresContextIOResult.from_value('a').apply(
... RequiresContextIOResult.from_failure(1),
... )(...) == IOFailure(1)
>>> assert RequiresContextIOResult.from_failure('a').apply(
... RequiresContextIOResult.from_value(transform),
... )(...) == IOFailure('a')
>>> assert RequiresContextIOResult.from_failure('a').apply(
... RequiresContextIOResult.from_failure('b'),
... )(...) == IOFailure('a')
container (KindN
[RequiresContextIOResult
, Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)], TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Composes this container with a function returning the same type.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess, IOFailure
>>> def first(lg: bool) -> RequiresContextIOResult[int, int, float]:
... # `deps` has `float` type here:
... return RequiresContextIOResult(
... lambda deps: IOSuccess(deps) if lg else IOFailure(-deps),
... )
>>> def second(
... number: int,
... ) -> RequiresContextIOResult[str, int, float]:
... # `deps` has `float` type here:
... return RequiresContextIOResult(
... lambda deps: IOSuccess('>=' if number >= deps else '<'),
... )
>>> assert first(True).bind(second)(1) == IOSuccess('>=')
>>> assert first(False).bind(second)(2) == IOFailure(-2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], KindN
[RequiresContextIOResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Alias for bind_context_ioresult method, it is the same as bind here.
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], KindN
[RequiresContextIOResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Binds Result
returning function to the current container.
>>> from returns.context import RequiresContextIOResult
>>> from returns.result import Success, Failure, Result
>>> from returns.io import IOSuccess, IOFailure
>>> def function(num: int) -> Result[int, str]:
... return Success(num + 1) if num > 0 else Failure('<0')
>>> assert RequiresContextIOResult.from_value(1).bind_result(
... function,
... )(RequiresContextIOResult.no_args) == IOSuccess(2)
>>> assert RequiresContextIOResult.from_value(0).bind_result(
... function,
... )(RequiresContextIOResult.no_args) == IOFailure('<0')
>>> assert RequiresContextIOResult.from_failure(':(').bind_result(
... function,
... )(RequiresContextIOResult.no_args) == IOFailure(':(')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Result
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
)]]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Binds RequiresContext
returning function to current container.
>>> from returns.context import RequiresContext
>>> from returns.io import IOSuccess, IOFailure
>>> def function(arg: int) -> RequiresContext[int, str]:
... return RequiresContext(lambda deps: len(deps) + arg)
>>> assert function(2)('abc') == 5
>>> assert RequiresContextIOResult.from_value(2).bind_context(
... function,
... )('abc') == IOSuccess(5)
>>> assert RequiresContextIOResult.from_failure(2).bind_context(
... function,
... )('abc') == IOFailure(2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], RequiresContext
[TypeVar
(_NewValueType
), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Binds RequiresContextResult
returning function to the current one.
>>> from returns.context import RequiresContextResult
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.result import Success, Failure
>>> def function(arg: int) -> RequiresContextResult[int, int, str]:
... if arg > 0:
... return RequiresContextResult(
... lambda deps: Success(len(deps) + arg),
... )
... return RequiresContextResult(
... lambda deps: Failure(len(deps) + arg),
... )
>>> assert function(2)('abc') == Success(5)
>>> assert function(-1)('abc') == Failure(2)
>>> assert RequiresContextIOResult.from_value(
... 2,
... ).bind_context_result(
... function,
... )('abc') == IOSuccess(5)
>>> assert RequiresContextIOResult.from_value(
... -1,
... ).bind_context_result(
... function,
... )('abc') == IOFailure(2)
>>> assert RequiresContextIOResult.from_failure(
... 2,
... ).bind_context_result(
... function,
... )('abc') == IOFailure(2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Binds IO
returning function to the current container.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IO, IOSuccess, IOFailure
>>> def function(number: int) -> IO[str]:
... return IO(str(number))
>>> assert RequiresContextIOResult.from_value(1).bind_io(
... function,
... )(RequiresContextIOResult.no_args) == IOSuccess('1')
>>> assert RequiresContextIOResult.from_failure(1).bind_io(
... function,
... )(RequiresContextIOResult.no_args) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], IO
[TypeVar
(_NewValueType
)]]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Binds IOResult
returning function to the current container.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOResult, IOSuccess, IOFailure
>>> def function(num: int) -> IOResult[int, str]:
... return IOSuccess(num + 1) if num > 0 else IOFailure('<0')
>>> assert RequiresContextIOResult.from_value(1).bind_ioresult(
... function,
... )(RequiresContextIOResult.no_args) == IOSuccess(2)
>>> assert RequiresContextIOResult.from_value(0).bind_ioresult(
... function,
... )(RequiresContextIOResult.no_args) == IOFailure('<0')
>>> assert RequiresContextIOResult.from_failure(':(').bind_ioresult(
... function,
... )(RequiresContextIOResult.no_args) == IOFailure(':(')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], IOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
)]]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Composes failed container with a pure function.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert RequiresContextIOResult.from_value(1).alt(
... lambda x: x + 1,
... )(...) == IOSuccess(1)
>>> assert RequiresContextIOResult.from_failure(1).alt(
... lambda x: x + 1,
... )(...) == IOFailure(2)
function (Callable
[[TypeVar
(_ErrorType
)], TypeVar
(_NewErrorType
)]) –
RequiresContextIOResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Composes this container with a function returning the same type.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess, IOFailure
>>> def lashable(
... arg: str,
... ) -> RequiresContextIOResult[str, str, str]:
... if len(arg) > 1:
... return RequiresContextIOResult(
... lambda deps: IOSuccess(deps + arg),
... )
... return RequiresContextIOResult(
... lambda deps: IOFailure(arg + deps),
... )
>>> assert RequiresContextIOResult.from_value('a').lash(
... lashable,
... )('c') == IOSuccess('a')
>>> assert RequiresContextIOResult.from_failure('a').lash(
... lashable,
... )('c') == IOFailure('ac')
>>> assert RequiresContextIOResult.from_failure('aa').lash(
... lashable,
... )('b') == IOSuccess('baa')
function (Callable
[[TypeVar
(_ErrorType
)], KindN
[RequiresContextIOResult
, TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextIOResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Composes inner Result
with ReaderIOResult
returning function.
Can be useful when you need an access to both states of the result.
>>> from returns.context import ReaderIOResult, NoDeps
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.result import Result
>>> def count(
... container: Result[int, int],
... ) -> ReaderIOResult[int, int, NoDeps]:
... return ReaderIOResult.from_result(
... container.map(lambda x: x + 1).alt(abs),
... )
>>> success = ReaderIOResult.from_value(1)
>>> failure = ReaderIOResult.from_failure(-1)
>>> assert success.compose_result(count)(...) == IOSuccess(2)
>>> assert failure.compose_result(count)(...) == IOFailure(1)
function (Callable
[[Result
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
)]], KindN
[RequiresContextIOResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Allows to modify the environment type.
>>> from returns.context import RequiresContextIOResultE
>>> from returns.io import IOSuccess, impure_safe
>>> def div(arg: int) -> RequiresContextIOResultE[float, int]:
... return RequiresContextIOResultE(
... impure_safe(lambda deps: arg / deps),
... )
>>> assert div(3).modify_env(int)('2') == IOSuccess(1.5)
>>> assert div(3).modify_env(int)('0').failure()
function (Callable
[[TypeVar
(_NewEnvType
)], TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContextIOResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
), TypeVar
(_NewEnvType
)]
Is used to get the current dependencies inside the call stack.
Similar to returns.context.requires_context.RequiresContext.ask()
,
but returns IOResult
instead of a regular value.
Please, refer to the docs there to learn how to use it.
One important note that is worth duplicating here:
you might need to provide _EnvType
explicitly,
so mypy
will know about it statically.
>>> from returns.context import RequiresContextIOResultE
>>> from returns.io import IOSuccess
>>> assert RequiresContextIOResultE[int, int].ask().map(
... str,
... )(1) == IOSuccess('1')
RequiresContextIOResult
[TypeVar
(_EnvType
, contravariant=True), TypeVar
(_ErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Creates new container with Result
as a unit value.
>>> from returns.context import RequiresContextIOResult
>>> from returns.result import Success, Failure
>>> from returns.io import IOSuccess, IOFailure
>>> deps = RequiresContextIOResult.no_args
>>> assert RequiresContextIOResult.from_result(
... Success(1),
... )(deps) == IOSuccess(1)
>>> assert RequiresContextIOResult.from_result(
... Failure(1),
... )(deps) == IOFailure(1)
inner_value (Result
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), Any
]
Creates new container from successful IO
value.
>>> from returns.io import IO, IOSuccess
>>> from returns.context import RequiresContextIOResult
>>> assert RequiresContextIOResult.from_io(IO(1))(
... RequiresContextIOResult.no_args,
... ) == IOSuccess(1)
inner_value (IO
[TypeVar
(_NewValueType
)]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), Any
, Any
]
Creates a new container from failed IO
value.
>>> from returns.io import IO, IOFailure
>>> from returns.context import RequiresContextIOResult
>>> assert RequiresContextIOResult.from_failed_io(IO(1))(
... RequiresContextIOResult.no_args,
... ) == IOFailure(1)
inner_value (IO
[TypeVar
(_NewErrorType
)]) –
RequiresContextIOResult
[Any
, TypeVar
(_NewErrorType
), Any
]
Creates new container with IOResult
as a unit value.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess, IOFailure
>>> deps = RequiresContextIOResult.no_args
>>> assert RequiresContextIOResult.from_ioresult(
... IOSuccess(1),
... )(deps) == IOSuccess(1)
>>> assert RequiresContextIOResult.from_ioresult(
... IOFailure(1),
... )(deps) == IOFailure(1)
inner_value (IOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), Any
]
Creates new container with ReaderIOResult
as a unit value.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert RequiresContextIOResult.from_ioresult_context(
... RequiresContextIOResult.from_value(1),
... )(...) == IOSuccess(1)
>>> assert RequiresContextIOResult.from_ioresult_context(
... RequiresContextIOResult.from_failure(1),
... )(...) == IOFailure(1)
inner_value (RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]
You might end up with RequiresContext[IOResult]
as a value.
This method is designed to turn it into RequiresContextIOResult
.
It will save all the typing information.
It is just more useful!
>>> from returns.context import RequiresContext
>>> from returns.io import IOSuccess, IOFailure
>>> assert RequiresContextIOResult.from_typecast(
... RequiresContext.from_value(IOSuccess(1)),
... )(RequiresContextIOResult.no_args) == IOSuccess(1)
>>> assert RequiresContextIOResult.from_typecast(
... RequiresContext.from_value(IOFailure(1)),
... )(RequiresContextIOResult.no_args) == IOFailure(1)
inner_value (RequiresContext
[IOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)], TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Creates new container from RequiresContext
as a success unit.
>>> from returns.context import RequiresContext
>>> from returns.io import IOSuccess
>>> assert RequiresContextIOResult.from_context(
... RequiresContext.from_value(1),
... )(...) == IOSuccess(1)
inner_value (RequiresContext
[TypeVar
(_NewValueType
), TypeVar
(_NewEnvType
)]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), Any
, TypeVar
(_NewEnvType
)]
Creates new container from RequiresContext
as a failure unit.
>>> from returns.context import RequiresContext
>>> from returns.io import IOFailure
>>> assert RequiresContextIOResult.from_failed_context(
... RequiresContext.from_value(1),
... )(...) == IOFailure(1)
inner_value (RequiresContext
[TypeVar
(_NewValueType
), TypeVar
(_NewEnvType
)]) –
RequiresContextIOResult
[Any
, TypeVar
(_NewValueType
), TypeVar
(_NewEnvType
)]
Creates new container from RequiresContextResult
as a unit value.
>>> from returns.context import RequiresContextResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert RequiresContextIOResult.from_result_context(
... RequiresContextResult.from_value(1),
... )(...) == IOSuccess(1)
>>> assert RequiresContextIOResult.from_result_context(
... RequiresContextResult.from_failure(1),
... )(...) == IOFailure(1)
inner_value (RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]
Creates new container with IOSuccess(inner_value)
as a unit value.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess
>>> assert RequiresContextIOResult.from_value(1)(
... RequiresContextIOResult.no_args,
... ) == IOSuccess(1)
inner_value (TypeVar
(_NewValueType
)) –
RequiresContextIOResult
[TypeVar
(_NewValueType
), Any
, Any
]
Creates new container with IOFailure(inner_value)
as a unit value.
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOFailure
>>> assert RequiresContextIOResult.from_failure(1)(
... RequiresContextIOResult.no_args,
... ) == IOFailure(1)
inner_value (TypeVar
(_NewErrorType
)) –
RequiresContextIOResult
[Any
, TypeVar
(_NewErrorType
), Any
]
Alias for a popular case when Result
has Exception
as error type.
alias of RequiresContextIOResult
[_ValueType
, Exception
, _EnvType
]
Alias to save you some typing. Uses original name from Haskell.
inner_value (Callable
[[TypeVar
(_EnvType
, contravariant=True)], IOResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
)]]) –
Alias to save you some typing. Uses Exception
as error type.
alias of RequiresContextIOResult
[_ValueType
, Exception
, _EnvType
]
Bases: BaseContainer
, SupportsKindN
[RequiresContextFutureResult
, _ValueType
, _ErrorType
, _EnvType
], ReaderFutureResultBasedN
[_ValueType
, _ErrorType
, _EnvType
], FutureResultLikeN
[_ValueType
, _ErrorType
, _EnvType
]
The RequiresContextFutureResult
combinator.
This probably the main type people are going to use in async
programs.
See returns.context.requires_context.RequiresContext
,
returns.context.requires_context_result.RequiresContextResult
,
and
returns.context.requires_context_result.RequiresContextIOResult
for more docs.
This is just a handy wrapper around
RequiresContext[FutureResult[a, b], env]
which represents a context-dependent impure async operation that might fail.
So, this is a thin wrapper, without any changes in logic. Why do we need this wrapper? That’s just for better usability!
This way RequiresContextIOResult
allows to simply work with:
raw values and pure functions
RequiresContext
values and pure functions returning it
RequiresContextResult
values and pure functions returning it
RequiresContextIOResult
values and pure functions returning it
Result
and pure functions returning it
IOResult
and functions returning it
FutureResult
and functions returning it
other RequiresContextFutureResult
related functions and values
This is a complex type for complex tasks!
Important implementation details
Due it is meaning, RequiresContextFutureResult
cannot have Success
and Failure
subclasses.
We only have just one type. That’s by design.
Different converters are also not supported for this type.
Use converters inside the RequiresContext
context, not outside.
See also
inner_value (Callable
[[TypeVar
(_EnvType
, contravariant=True)], FutureResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True)]]) –
A convenient placeholder to call methods created by .from_value().
Swaps value and error types.
So, values become errors and errors become values.
It is useful when you have to work with errors a lot.
And since we have a lot of .bind_
related methods
and only a single .lash
- it is easier to work with values.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> success = RequiresContextFutureResult.from_value(1)
>>> failure = RequiresContextFutureResult.from_failure(1)
>>> assert anyio.run(success.swap(), ...) == IOFailure(1)
>>> assert anyio.run(failure.swap(), ...) == IOSuccess(1)
RequiresContextFutureResult
[TypeVar
(_ErrorType
, covariant=True), TypeVar
(_ValueType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Composes successful container with a pure function.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert anyio.run(RequiresContextFutureResult.from_value(1).map(
... lambda x: x + 1,
... )(...).awaitable) == IOSuccess(2)
>>> assert anyio.run(RequiresContextFutureResult.from_failure(1).map(
... lambda x: x + 1,
... )(...).awaitable) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Calls a wrapped function in a container on this container.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> def transform(arg: str) -> str:
... return arg + 'b'
>>> assert anyio.run(
... RequiresContextFutureResult.from_value('a').apply(
... RequiresContextFutureResult.from_value(transform),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess('ab')
>>> assert anyio.run(
... RequiresContextFutureResult.from_failure('a').apply(
... RequiresContextFutureResult.from_value(transform),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure('a')
container (KindN
[RequiresContextFutureResult
, Callable
[[TypeVar
(_ValueType
, covariant=True)], TypeVar
(_NewValueType
)], TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Composes this container with a function returning the same type.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> def function(
... number: int,
... ) -> RequiresContextFutureResult[str, int, int]:
... # `deps` has `int` type here:
... return RequiresContextFutureResult(
... lambda deps: FutureResult.from_value(str(number + deps)),
... )
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(2).bind(function),
... 3,
... ) == IOSuccess('5')
>>> assert anyio.run(
... RequiresContextFutureResult.from_failure(2).bind(function),
... 3,
... ) == IOFailure(2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], KindN
[RequiresContextFutureResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Alias for bind_context_future_result method, it is the same as bind here.
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], KindN
[RequiresContextFutureResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Composes this container with a async function returning the same type.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> async def function(
... number: int,
... ) -> RequiresContextFutureResult[str, int, int]:
... return RequiresContextFutureResult.from_value(number + 1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(1).bind_async(
... function,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(2)
>>> assert anyio.run(
... RequiresContextFutureResult.from_failure(1).bind_async(
... function,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Awaitable
[KindN
[RequiresContextFutureResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Alias for bind_async_context_future_result method, it is the same as bind_async here.
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Awaitable
[KindN
[RequiresContextFutureResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Allows to compose a container and a regular async
function.
This function should return plain, non-container value.
See bind_async()
to bind async
function that returns a container.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> async def coroutine(x: int) -> int:
... return x + 1
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(1).bind_awaitable(
... coroutine,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(2)
>>> assert anyio.run(
... RequiresContextFutureResult.from_failure(1).bind_awaitable(
... coroutine,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Awaitable
[TypeVar
(_NewValueType
)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds Result
returning function to the current container.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.result import Success, Result
>>> from returns.io import IOSuccess, IOFailure
>>> def function(num: int) -> Result[int, str]:
... return Success(num + 1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(1).bind_result(
... function,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(2)
>>> assert anyio.run(
... RequiresContextFutureResult.from_failure(':(').bind_result(
... function,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(':(')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Result
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds RequiresContext
returning function to current container.
>>> import anyio
>>> from returns.context import RequiresContext
>>> from returns.io import IOSuccess, IOFailure
>>> def function(arg: int) -> RequiresContext[int, str]:
... return RequiresContext(lambda deps: len(deps) + arg)
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(2).bind_context(
... function,
... ),
... 'abc',
... ) == IOSuccess(5)
>>> assert anyio.run(
... RequiresContextFutureResult.from_failure(0).bind_context(
... function,
... ),
... 'abc',
... ) == IOFailure(0)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], RequiresContext
[TypeVar
(_NewValueType
), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds RequiresContextResult
returning function to the current one.
>>> import anyio
>>> from returns.context import RequiresContextResult
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.result import Success
>>> def function(arg: int) -> RequiresContextResult[int, int, str]:
... return RequiresContextResult(
... lambda deps: Success(len(deps) + arg),
... )
>>> instance = RequiresContextFutureResult.from_value(
... 2,
... ).bind_context_result(
... function,
... )('abc')
>>> assert anyio.run(instance.awaitable) == IOSuccess(5)
>>> instance = RequiresContextFutureResult.from_failure(
... 2,
... ).bind_context_result(
... function,
... )('abc')
>>> assert anyio.run(instance.awaitable) == IOFailure(2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds RequiresContextIOResult
returning function to the current one.
>>> import anyio
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess, IOFailure
>>> def function(arg: int) -> RequiresContextIOResult[int, int, str]:
... return RequiresContextIOResult(
... lambda deps: IOSuccess(len(deps) + arg),
... )
>>> instance = RequiresContextFutureResult.from_value(
... 2,
... ).bind_context_ioresult(
... function,
... )('abc')
>>> assert anyio.run(instance.awaitable) == IOSuccess(5)
>>> instance = RequiresContextFutureResult.from_failure(
... 2,
... ).bind_context_ioresult(
... function,
... )('abc')
>>> assert anyio.run(instance.awaitable) == IOFailure(2)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds IO
returning function to the current container.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IO, IOSuccess, IOFailure
>>> def do_io(number: int) -> IO[str]:
... return IO(str(number)) # not IO operation actually
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(1).bind_io(do_io),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess('1')
>>> assert anyio.run(
... RequiresContextFutureResult.from_failure(1).bind_io(do_io),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], IO
[TypeVar
(_NewValueType
)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds IOResult
returning function to the current container.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOResult, IOSuccess, IOFailure
>>> def function(num: int) -> IOResult[int, str]:
... return IOSuccess(num + 1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(1).bind_ioresult(
... function,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(2)
>>> assert anyio.run(
... RequiresContextFutureResult.from_failure(':(').bind_ioresult(
... function,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(':(')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], IOResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds Future
returning function to the current container.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.future import Future
>>> from returns.io import IOSuccess, IOFailure
>>> def function(num: int) -> Future[int]:
... return Future.from_value(num + 1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(1).bind_future(
... function,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(2)
>>> failed = RequiresContextFutureResult.from_failure(':(')
>>> assert anyio.run(
... failed.bind_future(function),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(':(')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Future
[TypeVar
(_NewValueType
)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds FutureResult
returning function to the current container.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> def function(num: int) -> FutureResult[int, str]:
... return FutureResult.from_value(num + 1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(1).bind_future_result(
... function,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(2)
>>> failed = RequiresContextFutureResult.from_failure(':(')
>>> assert anyio.run(
... failed.bind_future_result(function),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(':(')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Binds Future
returning async function to the current container.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.future import Future
>>> from returns.io import IOSuccess, IOFailure
>>> async def function(num: int) -> Future[int]:
... return Future.from_value(num + 1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(1).bind_async_future(
... function,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(2)
>>> failed = RequiresContextFutureResult.from_failure(':(')
>>> assert anyio.run(
... failed.bind_async_future(function),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(':(')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Awaitable
[Future
[TypeVar
(_NewValueType
)]]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Bind FutureResult
returning async function to the current container.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> async def function(num: int) -> FutureResult[int, str]:
... return FutureResult.from_value(num + 1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(
... 1,
... ).bind_async_future_result(
... function,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(2)
>>> failed = RequiresContextFutureResult.from_failure(':(')
>>> assert anyio.run(
... failed.bind_async_future_result(function),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(':(')
function (Callable
[[TypeVar
(_ValueType
, covariant=True)], Awaitable
[FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True)]]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Composes failed container with a pure function.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert anyio.run(
... RequiresContextFutureResult.from_value(1).alt(
... lambda x: x + 1,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_failure(1).alt(
... lambda x: x + 1,
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(2)
function (Callable
[[TypeVar
(_ErrorType
, covariant=True)], TypeVar
(_NewErrorType
)]) –
RequiresContextFutureResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Composes this container with a function returning the same type.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess
>>> def lashable(
... arg: str,
... ) -> RequiresContextFutureResult[str, str, str]:
... return RequiresContextFutureResult(
... lambda deps: FutureResult.from_value(
... deps + arg,
... ),
... )
>>> assert anyio.run(
... RequiresContextFutureResult.from_value('a').lash(lashable),
... 'c',
... ) == IOSuccess('a')
>>> assert anyio.run(
... RequiresContextFutureResult.from_failure('aa').lash(
... lashable,
... ),
... 'b',
... ) == IOSuccess('baa')
function (Callable
[[TypeVar
(_ErrorType
, covariant=True)], KindN
[RequiresContextFutureResult
, TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextFutureResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Composes inner Result
with ReaderFutureResult
returning func.
Can be useful when you need an access to both states of the result.
>>> import anyio
>>> from returns.context import ReaderFutureResult, NoDeps
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.result import Result
>>> def count(
... container: Result[int, int],
... ) -> ReaderFutureResult[int, int, NoDeps]:
... return ReaderFutureResult.from_result(
... container.map(lambda x: x + 1).alt(abs),
... )
>>> success = ReaderFutureResult.from_value(1)
>>> failure = ReaderFutureResult.from_failure(-1)
>>> assert anyio.run(
... success.compose_result(count), ReaderFutureResult.no_args,
... ) == IOSuccess(2)
>>> assert anyio.run(
... failure.compose_result(count), ReaderFutureResult.no_args,
... ) == IOFailure(1)
function (Callable
[[Result
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True)]], KindN
[RequiresContextFutureResult
, TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Allows to modify the environment type.
>>> import anyio
>>> from returns.future import future_safe, asyncify
>>> from returns.context import RequiresContextFutureResultE
>>> from returns.io import IOSuccess
>>> def div(arg: int) -> RequiresContextFutureResultE[float, int]:
... return RequiresContextFutureResultE(
... future_safe(asyncify(lambda deps: arg / deps)),
... )
>>> assert anyio.run(div(3).modify_env(int), '2') == IOSuccess(1.5)
>>> assert anyio.run(div(3).modify_env(int), '0').failure()
function (Callable
[[TypeVar
(_NewEnvType
)], TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContextFutureResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_NewEnvType
)]
Is used to get the current dependencies inside the call stack.
Similar to
returns.context.requires_context.RequiresContext.ask()
,
but returns FutureResult
instead of a regular value.
Please, refer to the docs there to learn how to use it.
One important note that is worth duplicating here:
you might need to provide type annotations explicitly,
so mypy
will know about it statically.
>>> import anyio
>>> from returns.context import RequiresContextFutureResultE
>>> from returns.io import IOSuccess
>>> assert anyio.run(
... RequiresContextFutureResultE[int, int].ask().map(str),
... 1,
... ) == IOSuccess('1')
RequiresContextFutureResult
[TypeVar
(_EnvType
, contravariant=True), TypeVar
(_ErrorType
, covariant=True), TypeVar
(_EnvType
, contravariant=True)]
Creates new container with Result
as a unit value.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.result import Success, Failure
>>> from returns.io import IOSuccess, IOFailure
>>> assert anyio.run(
... RequiresContextFutureResult.from_result(Success(1)),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_result(Failure(1)),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
inner_value (Result
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), Any
]
Creates new container from successful IO
value.
>>> import anyio
>>> from returns.io import IO, IOSuccess
>>> from returns.context import RequiresContextFutureResult
>>> assert anyio.run(
... RequiresContextFutureResult.from_io(IO(1)),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
inner_value (IO
[TypeVar
(_NewValueType
)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), Any
, Any
]
Creates a new container from failed IO
value.
>>> import anyio
>>> from returns.io import IO, IOFailure
>>> from returns.context import RequiresContextFutureResult
>>> assert anyio.run(
... RequiresContextFutureResult.from_failed_io(IO(1)),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
inner_value (IO
[TypeVar
(_NewErrorType
)]) –
RequiresContextFutureResult
[Any
, TypeVar
(_NewErrorType
), Any
]
Creates new container with IOResult
as a unit value.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert anyio.run(
... RequiresContextFutureResult.from_ioresult(IOSuccess(1)),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_ioresult(IOFailure(1)),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
inner_value (IOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), Any
]
Creates new container with successful Future
as a unit value.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.future import Future
>>> from returns.io import IOSuccess
>>> assert anyio.run(
... RequiresContextFutureResult.from_future(Future.from_value(1)),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
inner_value (Future
[TypeVar
(_NewValueType
)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), Any
, Any
]
Creates new container with failed Future
as a unit value.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.future import Future
>>> from returns.io import IOFailure
>>> assert anyio.run(
... RequiresContextFutureResult.from_failed_future(
... Future.from_value(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
inner_value (Future
[TypeVar
(_NewErrorType
)]) –
RequiresContextFutureResult
[Any
, TypeVar
(_NewErrorType
), Any
]
Creates new container with ReaderFutureResult
as a unit value.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert anyio.run(
... RequiresContextFutureResult.from_future_result_context(
... RequiresContextFutureResult.from_value(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_future_result_context(
... RequiresContextFutureResult.from_failure(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
inner_value (RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]
Creates new container with FutureResult
as a unit value.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert anyio.run(
... RequiresContextFutureResult.from_future_result(
... FutureResult.from_value(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_future_result(
... FutureResult.from_failure(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
inner_value (FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), Any
]
You might end up with RequiresContext[FutureResult]
as a value.
This method is designed to turn it into RequiresContextFutureResult
.
It will save all the typing information.
It is just more useful!
>>> import anyio
>>> from returns.context import RequiresContext
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert anyio.run(
... RequiresContextFutureResult.from_typecast(
... RequiresContext.from_value(FutureResult.from_value(1)),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_typecast(
... RequiresContext.from_value(FutureResult.from_failure(1)),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
inner_value (RequiresContext
[FutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
)], TypeVar
(_EnvType
, contravariant=True)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_EnvType
, contravariant=True)]
Creates new container from RequiresContext
as a success unit.
>>> import anyio
>>> from returns.context import RequiresContext
>>> from returns.io import IOSuccess
>>> assert anyio.run(
... RequiresContextFutureResult.from_context(
... RequiresContext.from_value(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
inner_value (RequiresContext
[TypeVar
(_NewValueType
), TypeVar
(_NewEnvType
)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), Any
, TypeVar
(_NewEnvType
)]
Creates new container from RequiresContext
as a failure unit.
>>> import anyio
>>> from returns.context import RequiresContext
>>> from returns.io import IOFailure
>>> assert anyio.run(
... RequiresContextFutureResult.from_failed_context(
... RequiresContext.from_value(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
inner_value (RequiresContext
[TypeVar
(_NewValueType
), TypeVar
(_NewEnvType
)]) –
RequiresContextFutureResult
[Any
, TypeVar
(_NewValueType
), TypeVar
(_NewEnvType
)]
Creates new container from RequiresContextResult
as a unit value.
>>> import anyio
>>> from returns.context import RequiresContextResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert anyio.run(
... RequiresContextFutureResult.from_result_context(
... RequiresContextResult.from_value(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_result_context(
... RequiresContextResult.from_failure(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
inner_value (RequiresContextResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]
Creates new container from RequiresContextIOResult
as a unit value.
>>> import anyio
>>> from returns.context import RequiresContextIOResult
>>> from returns.io import IOSuccess, IOFailure
>>> assert anyio.run(
... RequiresContextFutureResult.from_ioresult_context(
... RequiresContextIOResult.from_value(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOSuccess(1)
>>> assert anyio.run(
... RequiresContextFutureResult.from_ioresult_context(
... RequiresContextIOResult.from_failure(1),
... ),
... RequiresContextFutureResult.no_args,
... ) == IOFailure(1)
inner_value (RequiresContextIOResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]) –
RequiresContextFutureResult
[TypeVar
(_NewValueType
), TypeVar
(_NewErrorType
), TypeVar
(_NewEnvType
)]
Creates new container with successful FutureResult
as a unit value.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOSuccess
>>> assert anyio.run(RequiresContextFutureResult.from_value(1)(
... RequiresContextFutureResult.no_args,
... ).awaitable) == IOSuccess(1)
inner_value (TypeVar
(_FirstType
)) –
RequiresContextFutureResult
[TypeVar
(_FirstType
), Any
, Any
]
Creates new container with failed FutureResult
as a unit value.
>>> import anyio
>>> from returns.context import RequiresContextFutureResult
>>> from returns.io import IOFailure
>>> assert anyio.run(RequiresContextFutureResult.from_failure(1)(
... RequiresContextFutureResult.no_args,
... ).awaitable) == IOFailure(1)
inner_value (TypeVar
(_FirstType
)) –
RequiresContextFutureResult
[Any
, TypeVar
(_FirstType
), Any
]
Alias for a popular case when Result
has Exception
as error type.
alias of RequiresContextFutureResult
[_ValueType
, Exception
, _EnvType
]
Sometimes RequiresContextFutureResult is too long to type.
inner_value (Callable
[[TypeVar
(_EnvType
, contravariant=True)], FutureResult
[TypeVar
(_ValueType
, covariant=True), TypeVar
(_ErrorType
, covariant=True)]]) –
Alias to save you some typing. Uses Exception
as error type.
alias of RequiresContextFutureResult
[_ValueType
, Exception
, _EnvType
]
This tutorial will guide you through the process of creating your own containers.
First things first, why would anyone want to create a custom containers?
The great idea about “containers” in functional programming is that it can be literally anything. There are endless use-cases.
You can create your own primitives for working with some language-or-framework specific problem, or just model your business domain.
You can copy ideas from other languages or just compose existing containers
for better usability
(like IOResult
is the composition of IO
and Result
).
Example
We are going to implement a Pair
container for this example.
What is a Pair
? Well, it is literally a pair of two values.
No more, no less. Similar to a Tuple[FirstType, SecondType]
.
But with extra goodies.
Note
You can find all code samples here.
After you came up with the idea, you will need to make a decision: what capabilities my container must have?
Basically, you should decide what Interfaces you will subtype and what
methods and laws will be present in your type.
You can create just a returns.interfaces.mappable.MappableN
or choose a full featured returns.interfaces.container.ContainerN
.
You can also choose some specific interfaces to use,
like returns.interfaces.specific.result.ResultLikeN
or any other.
Summing up, decide what laws and methods you need to solve your problem. And then subtype the interfaces that provide these methods and laws.
Example
What interfaces a Pair
type needs?
returns.interfaces.equable.Equable
,
because two Pair
instances can be compared
returns.interfaces.mappable.MappableN
,
because the first type can be composed with pure functions
returns.interfaces.bindable.BindableN
,
because a Pair
can be bound to a function returning a new Pair
based on the first type
returns.interfaces.altable.AltableN
,
because the second type can be composed with pure functions
returns.interfaces.lashable.LashableN
,
because a Pair
can be bound to a function returning a new Pair
based on the second type
Now, after we know about all interfaces we would need, let’s find pre-defined aliases we can reuse.
Turns out, there are some of them!
returns.interfaces.bimappable.BiMappableN
which combines MappableN
and AltableN
returns.interfaces.swappable.SwappableN
is an alias for BiMappableN
with a new method called .swap
to change values order
Let’s look at the result:
Note
A special note on returns.primitives.container.BaseContainer
.
It is a very useful class with lots of pre-defined features, like:
immutability, better cloning, serialization, and comparison.
You can skip it if you wish, but it is highlighly recommended.
Later we will talk about an actual implementation of all required methods.
So, let’s start writing some code!
We would need to implement all interface methods,
otherwise mypy
won’t be happy.
That’s what it currently says on our type definition:
error: Final class test_pair1.Pair has abstract attributes "alt", "bind", "equals", "lash", "map", "swap"
Looks like it already knows what methods should be there!
Ok, let’s drop some initial and straight forward implementation. We will later make it more complex step by step.
1from typing import Callable, Tuple, TypeVar
2
3from typing_extensions import final
4
5from returns.interfaces import bindable, equable, lashable, swappable
6from returns.primitives.container import BaseContainer, container_equality
7from returns.primitives.hkt import Kind2, SupportsKind2, dekind
8
9_FirstType = TypeVar('_FirstType')
10_SecondType = TypeVar('_SecondType')
11
12_NewFirstType = TypeVar('_NewFirstType')
13_NewSecondType = TypeVar('_NewSecondType')
14
15
16@final
17class Pair(
18 BaseContainer,
19 SupportsKind2['Pair', _FirstType, _SecondType],
20 bindable.Bindable2[_FirstType, _SecondType],
21 swappable.Swappable2[_FirstType, _SecondType],
22 lashable.Lashable2[_FirstType, _SecondType],
23 equable.Equable,
24):
25 """
26 A type that represents a pair of something.
27
28 Like to coordinates ``(x, y)`` or two best friends.
29 Or a question and an answer.
30
31 """
32
33 def __init__(
34 self,
35 inner_value: Tuple[_FirstType, _SecondType],
36 ) -> None:
37 """Saves passed tuple as ``._inner_value`` inside this instance."""
38 super().__init__(inner_value)
39
40 # `Equable` part:
41
42 equals = container_equality # we already have this defined for all types
43
44 # `Mappable` part via `BiMappable`:
45
46 def map(
47 self,
48 function: Callable[[_FirstType], _NewFirstType],
49 ) -> 'Pair[_NewFirstType, _SecondType]':
50 return Pair((function(self._inner_value[0]), self._inner_value[1]))
51
52 # `BindableN` part:
53
54 def bind(
55 self,
56 function: Callable[
57 [_FirstType],
58 Kind2['Pair', _NewFirstType, _SecondType],
59 ],
60 ) -> 'Pair[_NewFirstType, _SecondType]':
61 return dekind(function(self._inner_value[0]))
62
63 # `AltableN` part via `BiMappableN`:
64
65 def alt(
66 self,
67 function: Callable[[_SecondType], _NewSecondType],
68 ) -> 'Pair[_FirstType, _NewSecondType]':
69 return Pair((self._inner_value[0], function(self._inner_value[1])))
70
71 # `LashableN` part:
72
73 def lash(
74 self,
75 function: Callable[
76 [_SecondType],
77 Kind2['Pair', _FirstType, _NewSecondType],
78 ],
79 ) -> 'Pair[_FirstType, _NewSecondType]':
80 return dekind(function(self._inner_value[1]))
81
82 # `SwappableN` part:
83
84 def swap(self) -> 'Pair[_SecondType, _FirstType]':
85 return Pair((self._inner_value[1], self._inner_value[0]))
You can check our resulting source with mypy
. It would be happy this time.
As you can see our existing interfaces do not cover everything. We can potentially want several extra things:
A method that takes two arguments and returns a new Pair
instance
A named constructor to create a Pair
from a single value
A named constructor to create a Pair
from two values
We can define an interface just for this! It would be also nice to add all other interfaces there as supertypes.
That’s how it is going to look:
1class PairLikeN(
2 bindable.BindableN[_FirstType, _SecondType, _ThirdType],
3 swappable.SwappableN[_FirstType, _SecondType, _ThirdType],
4 lashable.LashableN[_FirstType, _SecondType, _ThirdType],
5 equable.Equable,
6):
7 """Special interface for types that look like a ``Pair``."""
8
9 @abstractmethod
10 def pair(
11 self: _PairLikeKind,
12 function: Callable[
13 [_FirstType, _SecondType],
14 KindN[_PairLikeKind, _NewFirstType, _NewSecondType, _ThirdType],
15 ],
16 ) -> KindN[_PairLikeKind, _NewFirstType, _NewSecondType, _ThirdType]:
17 """Allows to work with both arguments at the same time."""
18
19 @classmethod
20 @abstractmethod
21 def from_paired(
22 cls: Type[_PairLikeKind],
23 first: _NewFirstType,
24 second: _NewSecondType,
25 ) -> KindN[_PairLikeKind, _NewFirstType, _NewSecondType, _ThirdType]:
26 """Allows to create a PairLikeN from just two values."""
27
28 @classmethod
29 @abstractmethod
30 def from_unpaired(
31 cls: Type[_PairLikeKind],
32 inner_value: _NewFirstType,
33 ) -> KindN[_PairLikeKind, _NewFirstType, _NewFirstType, _ThirdType]:
34 """Allows to create a PairLikeN from just a single object."""
Awesome! Now we have a new interface to implement. Let’s do that!
1 def pair(
2 self,
3 function: Callable[
4 [_FirstType, _SecondType],
5 Kind2['Pair', _NewFirstType, _NewSecondType],
6 ],
7 ) -> 'Pair[_NewFirstType, _NewSecondType]':
8 return dekind(function(self._inner_value[0], self._inner_value[1]))
1 @classmethod
2 def from_unpaired(
3 cls,
4 inner_value: _NewFirstType,
5 ) -> 'Pair[_NewFirstType, _NewFirstType]':
6 return Pair((inner_value, inner_value))
Looks like we are done!
The best part about this type is that it is pure. So, we can write our tests inside docs!
We are going to use doctests builtin module for that.
This gives us several key benefits:
All our docs has usage examples
All our examples are correct, because they are executed and tested
We don’t need to write regular boring tests
Let’s add docs and doctests! Let’s use map
method as a short example:
1 def map(
2 self,
3 function: Callable[[_FirstType], _NewFirstType],
4 ) -> 'Pair[_NewFirstType, _SecondType]':
5 """
6 Changes the first type with a pure function.
7
8 >>> assert Pair((1, 2)).map(str) == Pair(('1', 2))
9
10 """
11 return Pair((function(self._inner_value[0]), self._inner_value[1]))
By adding these simple tests we would already have 100% coverage. But, what if we can completely skip writing tests, but still have 100%?
Let’s discuss how we can achieve that with “Laws as values”.
We already ship lots of laws with our interfaces. See our docs on laws and checking them.
Moreover, you can also define your own laws!
Let’s add them to our PairLikeN
interface.
Let’s start with laws definition:
1class _LawSpec(LawSpecDef):
2 @law_definition
3 def pair_equality_law(
4 raw_value: _FirstType,
5 container: 'PairLikeN[_FirstType, _SecondType, _ThirdType]',
6 ) -> None:
7 """Ensures that unpaired and paired constructors work fine."""
8 assert_equal(
9 container.from_unpaired(raw_value),
10 container.from_paired(raw_value, raw_value),
11 )
12
13 @law_definition
14 def pair_left_identity_law(
15 pair: Tuple[_FirstType, _SecondType],
16 container: 'PairLikeN[_FirstType, _SecondType, _ThirdType]',
17 function: Callable[
18 [_FirstType, _SecondType],
19 KindN['PairLikeN', _NewFirstType, _NewSecondType, _ThirdType],
20 ],
21 ) -> None:
22 """Ensures that unpaired and paired constructors work fine."""
23 assert_equal(
24 container.from_paired(*pair).pair(function),
25 function(*pair),
26 )
And them let’s add them to our PairLikeN
interface:
1class PairLikeN(
2 bindable.BindableN[_FirstType, _SecondType, _ThirdType],
3 swappable.SwappableN[_FirstType, _SecondType, _ThirdType],
4 lashable.LashableN[_FirstType, _SecondType, _ThirdType],
5 equable.Equable,
6):
7 """Special interface for types that look like a ``Pair``."""
8
9 _laws: ClassVar[Sequence[Law]] = (
10 Law2(_LawSpec.pair_equality_law),
11 Law3(_LawSpec.pair_left_identity_law),
12 )
13
14 @abstractmethod
15 def pair(
16 self: _PairLikeKind,
17 function: Callable[
18 [_FirstType, _SecondType],
19 KindN[_PairLikeKind, _NewFirstType, _NewSecondType, _ThirdType],
20 ],
21 ) -> KindN[_PairLikeKind, _NewFirstType, _NewSecondType, _ThirdType]:
22 """Allows to work with both arguments at the same time."""
23
24 @classmethod
25 @abstractmethod
26 def from_paired(
27 cls: Type[_PairLikeKind],
28 first: _NewFirstType,
29 second: _NewSecondType,
30 ) -> KindN[_PairLikeKind, _NewFirstType, _NewSecondType, _ThirdType]:
31 """Allows to create a PairLikeN from just two values."""
32
33 @classmethod
34 @abstractmethod
35 def from_unpaired(
36 cls: Type[_PairLikeKind],
37 inner_value: _NewFirstType,
38 ) -> KindN[_PairLikeKind, _NewFirstType, _NewFirstType, _ThirdType]:
39 """Allows to create a PairLikeN from just a single object."""
The last to do is to call check_all_laws(Pair, use_init=True)
to generate 10 hypothesis
test cases with hundreds real test cases inside.
Here’s the final result of our brand new Pair
type:
1from abc import abstractmethod
2from typing import Callable, ClassVar, NoReturn, Sequence, Tuple, Type, TypeVar
3
4from typing_extensions import final
5
6from returns.contrib.hypothesis.laws import check_all_laws
7from returns.interfaces import bindable, equable, lashable, swappable
8from returns.primitives.asserts import assert_equal
9from returns.primitives.container import BaseContainer, container_equality
10from returns.primitives.hkt import Kind2, KindN, SupportsKind2, dekind
11from returns.primitives.laws import Law, Law2, Law3, LawSpecDef, law_definition
12
13_FirstType = TypeVar('_FirstType')
14_SecondType = TypeVar('_SecondType')
15_ThirdType = TypeVar('_ThirdType')
16
17_NewFirstType = TypeVar('_NewFirstType')
18_NewSecondType = TypeVar('_NewSecondType')
19
20_PairLikeKind = TypeVar('_PairLikeKind', bound='PairLikeN')
21
22
23class _LawSpec(LawSpecDef):
24 @law_definition
25 def pair_equality_law(
26 raw_value: _FirstType,
27 container: 'PairLikeN[_FirstType, _SecondType, _ThirdType]',
28 ) -> None:
29 """Ensures that unpaired and paired constructors work fine."""
30 assert_equal(
31 container.from_unpaired(raw_value),
32 container.from_paired(raw_value, raw_value),
33 )
34
35 @law_definition
36 def pair_left_identity_law(
37 pair: Tuple[_FirstType, _SecondType],
38 container: 'PairLikeN[_FirstType, _SecondType, _ThirdType]',
39 function: Callable[
40 [_FirstType, _SecondType],
41 KindN['PairLikeN', _NewFirstType, _NewSecondType, _ThirdType],
42 ],
43 ) -> None:
44 """Ensures that unpaired and paired constructors work fine."""
45 assert_equal(
46 container.from_paired(*pair).pair(function),
47 function(*pair),
48 )
49
50
51class PairLikeN(
52 bindable.BindableN[_FirstType, _SecondType, _ThirdType],
53 swappable.SwappableN[_FirstType, _SecondType, _ThirdType],
54 lashable.LashableN[_FirstType, _SecondType, _ThirdType],
55 equable.Equable,
56):
57 """Special interface for types that look like a ``Pair``."""
58
59 _laws: ClassVar[Sequence[Law]] = (
60 Law2(_LawSpec.pair_equality_law),
61 Law3(_LawSpec.pair_left_identity_law),
62 )
63
64 @abstractmethod
65 def pair(
66 self: _PairLikeKind,
67 function: Callable[
68 [_FirstType, _SecondType],
69 KindN[_PairLikeKind, _NewFirstType, _NewSecondType, _ThirdType],
70 ],
71 ) -> KindN[_PairLikeKind, _NewFirstType, _NewSecondType, _ThirdType]:
72 """Allows to work with both arguments at the same time."""
73
74 @classmethod
75 @abstractmethod
76 def from_paired(
77 cls: Type[_PairLikeKind],
78 first: _NewFirstType,
79 second: _NewSecondType,
80 ) -> KindN[_PairLikeKind, _NewFirstType, _NewSecondType, _ThirdType]:
81 """Allows to create a PairLikeN from just two values."""
82
83 @classmethod
84 @abstractmethod
85 def from_unpaired(
86 cls: Type[_PairLikeKind],
87 inner_value: _NewFirstType,
88 ) -> KindN[_PairLikeKind, _NewFirstType, _NewFirstType, _ThirdType]:
89 """Allows to create a PairLikeN from just a single object."""
90
91
92PairLike2 = PairLikeN[_FirstType, _SecondType, NoReturn]
93PairLike3 = PairLikeN[_FirstType, _SecondType, _ThirdType]
94
95
96@final
97class Pair(
98 BaseContainer,
99 SupportsKind2['Pair', _FirstType, _SecondType],
100 PairLike2[_FirstType, _SecondType],
101):
102 """
103 A type that represents a pair of something.
104
105 Like to coordinates ``(x, y)`` or two best friends.
106 Or a question and an answer.
107
108 """
109
110 def __init__(
111 self,
112 inner_value: Tuple[_FirstType, _SecondType],
113 ) -> None:
114 """Saves passed tuple as ``._inner_value`` inside this instance."""
115 super().__init__(inner_value)
116
117 # `Equable` part:
118
119 equals = container_equality # we already have this defined for all types
120
121 # `Mappable` part via `BiMappable`:
122
123 def map(
124 self,
125 function: Callable[[_FirstType], _NewFirstType],
126 ) -> 'Pair[_NewFirstType, _SecondType]':
127 """
128 Changes the first type with a pure function.
129
130 >>> assert Pair((1, 2)).map(str) == Pair(('1', 2))
131
132 """
133 return Pair((function(self._inner_value[0]), self._inner_value[1]))
134
135 # `BindableN` part:
136
137 def bind(
138 self,
139 function: Callable[
140 [_FirstType],
141 Kind2['Pair', _NewFirstType, _SecondType],
142 ],
143 ) -> 'Pair[_NewFirstType, _SecondType]':
144 """
145 Changes the first type with a function returning another ``Pair``.
146
147 >>> def bindable(first: int) -> Pair[str, str]:
148 ... return Pair((str(first), ''))
149
150 >>> assert Pair((1, 'b')).bind(bindable) == Pair(('1', ''))
151
152 """
153 return dekind(function(self._inner_value[0]))
154
155 # `AltableN` part via `BiMappableN`:
156
157 def alt(
158 self,
159 function: Callable[[_SecondType], _NewSecondType],
160 ) -> 'Pair[_FirstType, _NewSecondType]':
161 """
162 Changes the second type with a pure function.
163
164 >>> assert Pair((1, 2)).alt(str) == Pair((1, '2'))
165
166 """
167 return Pair((self._inner_value[0], function(self._inner_value[1])))
168
169 # `LashableN` part:
170
171 def lash(
172 self,
173 function: Callable[
174 [_SecondType],
175 Kind2['Pair', _FirstType, _NewSecondType],
176 ],
177 ) -> 'Pair[_FirstType, _NewSecondType]':
178 """
179 Changes the second type with a function returning ``Pair``.
180
181 >>> def lashable(second: int) -> Pair[str, str]:
182 ... return Pair(('', str(second)))
183
184 >>> assert Pair(('a', 2)).lash(lashable) == Pair(('', '2'))
185
186 """
187 return dekind(function(self._inner_value[1]))
188
189 # `SwappableN` part:
190
191 def swap(self) -> 'Pair[_SecondType, _FirstType]':
192 """
193 Swaps ``Pair`` elements.
194
195 >>> assert Pair((1, 2)).swap() == Pair((2, 1))
196
197 """
198 return Pair((self._inner_value[1], self._inner_value[0]))
199
200 # `PairLikeN` part:
201
202 def pair(
203 self,
204 function: Callable[
205 [_FirstType, _SecondType],
206 Kind2['Pair', _NewFirstType, _NewSecondType],
207 ],
208 ) -> 'Pair[_NewFirstType, _NewSecondType]':
209 """
210 Creates a new ``Pair`` from an existing one via a passed function.
211
212 >>> def min_max(first: int, second: int) -> Pair[int, int]:
213 ... return Pair((min(first, second), max(first, second)))
214
215 >>> assert Pair((2, 1)).pair(min_max) == Pair((1, 2))
216 >>> assert Pair((1, 2)).pair(min_max) == Pair((1, 2))
217
218 """
219 return dekind(function(self._inner_value[0], self._inner_value[1]))
220
221 @classmethod
222 def from_paired(
223 cls,
224 first: _NewFirstType,
225 second: _NewSecondType,
226 ) -> 'Pair[_NewFirstType, _NewSecondType]':
227 """
228 Creates a new pair from two values.
229
230 >>> assert Pair.from_paired(1, 2) == Pair((1, 2))
231
232 """
233 return Pair((first, second))
234
235 @classmethod
236 def from_unpaired(
237 cls,
238 inner_value: _NewFirstType,
239 ) -> 'Pair[_NewFirstType, _NewFirstType]':
240 """
241 Creates a new pair from a single value.
242
243 >>> assert Pair.from_unpaired(1) == Pair((1, 1))
244
245 """
246 return Pair((inner_value, inner_value))
247
248
249# Running hypothesis auto-generated tests:
250check_all_laws(Pair, use_init=True)
Note
You can find all type-tests here.
The next thing we want is to write a type-test!
What is a type-test? This is a special type of tests for your typing.
We run mypy
on top of tests and use snapshots to assert the result.
We recommend to use pytest-mypy-plugins. Read more about how to use it.
Let’s start with a simple test
to make sure our .pair
function works correctly:
Warning
Please, don’t use env:
property the way we do here.
We need it since we store our example in tests/
folder.
And we have to tell mypy
how to find it.
1- case: test_pair_type
2 disable_cache: false
3 env:
4 # We only need this because we store this example in `tests/`
5 # and not in our source code. Please, do not copy this line!
6 - MYPYPATH=./tests/test_examples/test_your_container
7
8 # TODO: remove this config after
9 # mypy/typeshed/stdlib/unittest/mock.pyi:120:
10 # error: Class cannot subclass "Any" (has type "Any")
11 # is fixed.
12 mypy_config:
13 disallow_subclassing_any = False
14 main: |
15 # Let's import our `Pair` type we defined earlier:
16 from test_pair4 import Pair
17
18 reveal_type(Pair)
19
20 def function(first: int, second: str) -> Pair[float, bool]:
21 ...
22
23 my_pair: Pair[int, str] = Pair.from_paired(1, 'a')
24 reveal_type(my_pair.pair(function))
25 out: |
26 main:4: note: Revealed type is "def [_FirstType, _SecondType] (inner_value: Tuple[_FirstType`1, _SecondType`2]) -> test_pair4.Pair[_FirstType`1, _SecondType`2]"
27 main:10: note: Revealed type is "test_pair4.Pair[builtins.float, builtins.bool]"
Ok, now, let’s try to raise an error by using it incorrectly:
1- case: test_pair_error
2 disable_cache: false
3 env:
4 # We only need this because we store this example in `tests/`
5 # and not in our source code. Please, do not copy this line!
6 - MYPYPATH=./tests/test_examples/test_your_container
7
8 # TODO: remove this config after
9 # mypy/typeshed/stdlib/unittest/mock.pyi:120:
10 # error: Class cannot subclass "Any" (has type "Any")
11 # is fixed.
12 mypy_config:
13 disallow_subclassing_any = False
14 main: |
15 # Let's import our `Pair` type we defined earlier:
16 from test_pair4 import Pair
17
18 # Oups! This function has first and second types swapped!
19 def function(first: str, second: int) -> Pair[float, bool]:
20 ...
21
22 my_pair = Pair.from_paired(1, 'a')
23 my_pair.pair(function) # this should and will error
24 out: |
25 main:9: error: Argument 1 to "pair" of "Pair" has incompatible type "Callable[[str, int], Pair[float, bool]]"; expected "Callable[[int, str], KindN[Pair[Any, Any], float, bool, Any]]" [arg-type]
The last (but not the least!) thing you need
to know is that you can reuse all code
we already have for this new Pair
type.
This is because of our Higher Kinded Types feature.
So, let’s say we want to use native map_()
pointfree function with our new Pair
type.
Let’s test that it will work correctly:
1- case: test_pair_map
2 disable_cache: false
3 env:
4 # We only need this because we store this example in `tests/`
5 # and not in our source code. Please, do not copy this line!
6 - MYPYPATH=./tests/test_examples/test_your_container
7
8 # TODO: remove this config after
9 # mypy/typeshed/stdlib/unittest/mock.pyi:120:
10 # error: Class cannot subclass "Any" (has type "Any")
11 # is fixed.
12 mypy_config:
13 disallow_subclassing_any = False
14 main: |
15 from test_pair4 import Pair
16 from returns.pointfree import map_
17
18 my_pair: Pair[int, int] = Pair.from_unpaired(1)
19 reveal_type(my_pair.map(str))
20 reveal_type(map_(str)(my_pair))
21 out: |
22 main:5: note: Revealed type is "test_pair4.Pair[builtins.str, builtins.int]"
23 main:6: note: Revealed type is "test_pair4.Pair[builtins.str, builtins.int]"
Yes, it works!
Now you have fully working, typed, documented, lawful, and tested primitive. You can build any other primitive you need for your business logic or infrastructure.
The main idea behind functional programming is functional composition.
We provide several tools to make composition easy, readable, pythonic, and useful.
Note
Make sure you are familiar with our Pointfree tools, because pipelines and pointfree functions are best friends!
flow
allows to easily compose multiple functions together into a pipeline.
It is useful when you already have an instance to compose functions with.
Note
flow
is the recommended way to write your code with returns
!
Let’s see an example:
>>> from returns.pipeline import flow
>>> assert flow(
... [1, 2, 3],
... lambda collection: max(collection),
... lambda max_number: -max_number,
... ) == -3
This allows you to write declarative steps that should be performed on an existing value.
Note
Technical note: flow
has the best type inference mechanism
among all other tools we provide here.
This happens due to our mypy plugins.
You can also use flow
with pointfree functions and containers:
>>> from returns.result import Result, Success, Failure
>>> from returns.pointfree import bind
>>> from returns.pipeline import flow
>>> def regular_function(arg: int) -> float:
... return float(arg)
>>> def returns_container(arg: float) -> Result[str, ValueError]:
... if arg != 0:
... return Success(str(arg))
... return Failure(ValueError('Wrong arg'))
>>> def also_returns_container(arg: str) -> Result[str, ValueError]:
... return Success(arg + '!')
>>> assert flow(
... 1, # initial value
... regular_function, # composes easily
... returns_container, # also composes easily, but returns a container
... # So we need to `bind` the next function to allow it to consume
... # the container from the previous step.
... bind(also_returns_container),
... ) == Success('1.0!')
>>> # And this will fail:
>>> assert flow(
... 0, # initial value
... regular_function, # composes easily
... returns_container, # also composes easily, but returns a container
... # So we need to `bind` the next function to allow it to consume
... # the container from the previous step.
... bind(also_returns_container),
... ).failure().args == ('Wrong arg', )
And now let’s get to know pipe
, it is very similar,
but has different usage pattern.
pipe
is an easy way to compose functions together.
It is useful when you don’t have an instance to compose functions with yet.
Note
pipe
requires to use our mypy plugins.
Let’s see an example.
>>> from returns.pipeline import pipe
>>> pipeline = pipe(str, lambda x: x + 'b', str.upper)
>>> assert pipeline(1) == '1B'
It might be later used with multiple values:
>>> assert pipeline(2) == '2B'
It is also might be useful to compose containers together:
>>> from returns.pipeline import pipe
>>> from returns.result import Result, Success, Failure
>>> from returns.pointfree import bind
>>> def regular_function(arg: int) -> float:
... return float(arg)
>>> def returns_container(arg: float) -> Result[str, ValueError]:
... if arg != 0:
... return Success(str(arg))
... return Failure(ValueError('Wrong arg'))
>>> def also_returns_container(arg: str) -> Result[str, ValueError]:
... return Success(arg + '!')
>>> transaction = pipe(
... regular_function, # composes easily
... returns_container, # also composes easily, but returns a container
... # So we need to `bind` the next function to allow it to consume
... # the container from the previous step.
... bind(also_returns_container),
... )
>>> result = transaction(1) # running the pipeline
>>> assert result == Success('1.0!')
You might consider pipe()
as returns.functions.compose()
on steroids.
The main difference is that compose
takes strictly two arguments
(or you might say that it has an arity of two),
while pipe
has infinite possible arguments.
A really common task is to work with something stateful, like database connections or files.
First, you need to acquire some resource, then use it and do your thing, and clear things up and release the acquired resource.
There are several rules here:
If the acquiring failed, then do nothing: do not try to use the resource or release it
If the resource is acquired, then try to use it and then release it despite of the usage result
In other words, if you cannot open a file, then do nothing. If you opened it, then try to read it. And then always close it.
Let’s say you have to read a file’s contents:
>>> from typing import TextIO
>>> from returns.pipeline import managed, is_successful
>>> from returns.result import ResultE
>>> from returns.io import IOResultE, impure_safe
>>> def read_file(file_obj: TextIO) -> IOResultE[str]:
... return impure_safe(file_obj.read)() # this will be the final result
>>> def close_file(
... file_obj: TextIO,
... file_contents: ResultE[str],
... ) -> IOResultE[None]: # sometimes might require to use `untap`
... return impure_safe(file_obj.close)() # this value will be dropped
>>> managed_read = managed(read_file, close_file)
>>> read_result = managed_read(
... impure_safe(lambda filename: open(filename, 'r'))('pyproject.toml'),
... )
>>> assert is_successful(read_result) # file content is inside `IOSuccess`
And here’s how we recommend to combine
managed
with other pipeline functions:
>>> import tomlkit
>>> from returns.pipeline import flow
>>> from returns.pointfree import bind_result
>>> from returns.result import safe
>>> from returns.io import IOSuccess
>>> @safe
... def parse_toml(file_contents: str) -> dict:
... return tomlkit.parse(file_contents)
>>> @safe
... def get_project_name(parsed: dict) -> str:
... return parsed['tool']['poetry']['name']
>>> pipeline_result = flow(
... 'pyproject.toml', # filename we work with
... impure_safe(lambda filename: open(filename, 'r')),
... managed_read,
... bind_result(parse_toml),
... bind_result(get_project_name),
... )
>>> assert pipeline_result == IOSuccess('returns')
Notice a few tricks here:
We use managed
with and without flow
here,
both are fine!
We have created a managed_read
managed function,
so we don’t need to specify it every time we want
to read a file in a functional way
We are using impure and pure operations inside the pipeline: this helps us to understand how our app works. Which parts do access the file system and which just work
However, you can still use the imperative approach
with with:
or try/finally
wrapped into @impure_safe
decorator,
your choice! We don’t recommend to mix these two.
Stick to one you like the most.
managed
can be used with:
IOResult
FutureResult
RequiresContextIOResult
RequiresContextFutureResult
is_successful
is used to
tell whether or not your result is a success.
We treat only three types that do not throw as successful ones,
basically: Success
,
IOSuccess
,
and Some
>>> from returns.result import Success, Failure
>>> from returns.pipeline import is_successful
>>> assert is_successful(Success(1)) is True
>>> assert is_successful(Failure('text')) is False
Allows to compose a value and up to multiple functions that use this value.
All starts with the value itself. Each next function uses the previous result as an input parameter.
We use a custom mypy
plugin to make sure types are correct.
Otherwise, it is currently impossible to properly type this function.
Currently, flow
has a hard limit of 21 steps.
Because, it is not possible to type it otherwise.
We need a hard limit.
See: https://github.com/dry-python/returns/issues/461
Here’s how it should be used:
>>> from returns.pipeline import flow
>>> # => executes: str(float(int('1')))
>>> assert flow('1', int, float, str) == '1.0'
This function is closely related
to pipe
:
>>> from returns.pipeline import pipe
>>> assert flow('1', int, float, str) == pipe(int, float, str)('1')
See also
Requires our mypy plugin.
instance (TypeVar
(_InstanceType
)) –
functions (TypeVar
(_PipelineStepType
)) –
TypeVar
(_ReturnType
)
Allows to compose a value and up to 7 functions that use this value.
We use a custom mypy
plugin to make sure types are correct.
Otherwise, it is currently impossible to properly type this function.
Each next function uses the previous result as an input parameter. Here’s how it should be used:
>>> from returns.pipeline import pipe
>>> # => executes: str(float(int('1')))
>>> assert pipe(int, float, str)('1') == '1.0'
This function is closely related
to pipe
:
>>> from returns.pipeline import flow
>>> assert pipe(int, float, str)('1') == flow('1', int, float, str)
Allows to run managed computation.
Managed computations consist of three steps:
acquire
when we get some initial resource to work with
use
when the main logic is done
release
when we release acquired resource
Let’s look at the example:
We need to acquire an opened file to read it later
We need to use acquired file to read its content
We need to release the acquired file in the end
Here’s a code example:
>>> from returns.pipeline import managed
>>> from returns.io import IOSuccess, IOFailure, impure_safe
>>> class Lock(object):
... '''Example class to emulate state to acquire and release.'''
... def __init__(self, default: bool = False) -> None:
... self.set = default
... def __eq__(self, lock) -> bool: # we need this for testing
... return self.set == lock.set
... def release(self) -> None:
... self.set = False
>>> pipeline = managed(
... lambda lock: IOSuccess(lock) if lock.set else IOFailure(False),
... lambda lock, use_result: impure_safe(lock.release)(),
... )
>>> assert pipeline(IOSuccess(Lock(True))) == IOSuccess(Lock(False))
>>> assert pipeline(IOSuccess(Lock())) == IOFailure(False)
>>> assert pipeline(IOFailure('no lock')) == IOFailure('no lock')
See also
Implementation
This class requires some explanation.
First of all, we modeled this function as a class, so it can be partially applied easily.
Secondly, we used imperative approach of programming inside this class. Functional approached was 2 times slower. And way more complex to read and understand.
Lastly, we try to hide these two things for the end user. We pretend that this is not a class, but a function. We also do not break a functional abstraction for the end user. It is just an implementation detail.
Type inference does not work so well with lambda
functions.
But, we do not recommend to use this function with lambda
functions.
use (Callable
[[TypeVar
(_FirstType
)], KindN
[TypeVar
(_IOResultLikeType
, bound= IOResultLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
release (Callable
[[TypeVar
(_FirstType
), Result
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
)]], KindN
[TypeVar
(_IOResultLikeType
, bound= IOResultLikeN
), None
, TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_IOResultLikeType
, bound= IOResultLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_IOResultLikeType
, bound= IOResultLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Determines if a container was successful or not.
>>> from returns.maybe import Some, Nothing
>>> from returns.result import Failure, Success
>>> from returns.io import IOSuccess, IOFailure
>>> assert is_successful(Some(1))
>>> assert not is_successful(Nothing)
>>> assert is_successful(Success(1))
>>> assert not is_successful(Failure(1))
>>> assert is_successful(IOSuccess(1))
>>> assert not is_successful(IOFailure(1))
This function can work with containers
that are instance of returns.interfaces.unwrappable.Unwrappable
.
container (Unwrappable
) –
bool
We have several helpers to convert containers from one type to another and back again.
We have two converters to work with Result <-> Maybe
transformations:
maybe_to_result()
that converts Maybe
to Result
result_to_maybe()
that converts Result
to Maybe
That’s how they work:
>>> from returns.converters import maybe_to_result, result_to_maybe
>>> from returns.maybe import Maybe, Some
>>> from returns.result import Result, Success
>>> result: Result[int, Exception] = Success(1)
>>> maybe: Maybe[int] = result_to_maybe(result)
>>> assert maybe == Some(1)
>>> new_result: Result[int, None] = maybe_to_result(maybe)
>>> assert new_result == Success(1)
Take a note, that type changes.
Also, take a note that Success(None)
will be converted to Nothing
.
You can also use
flatten
to merge nested containers together:
>>> from returns.converters import flatten
>>> from returns.maybe import Some
>>> from returns.result import Success
>>> from returns.io import IO
>>> assert flatten(IO(IO(1))) == IO(1)
>>> assert flatten(Some(Some(1))) == Some(1)
>>> assert flatten(Success(Success(1))) == Success(1)
Joins two nested containers together.
Please, note that it will not join
two Failure
for Result
case
or two Nothing
for Maybe
case
(or basically any two error types) together.
>>> from returns.converters import flatten
>>> from returns.io import IO
>>> from returns.result import Failure, Success
>>> assert flatten(IO(IO(1))) == IO(1)
>>> assert flatten(Success(Success(1))) == Success(1)
>>> assert flatten(Failure(Failure(1))) == Failure(Failure(1))
See also
container (KindN
[TypeVar
(_BindableKind
, bound= BindableN
), KindN
[TypeVar
(_BindableKind
, bound= BindableN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)], TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
KindN
[TypeVar
(_BindableKind
, bound= BindableN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Converts Result
container to Maybe
container.
>>> from returns.maybe import Some, Nothing
>>> from returns.result import Failure, Success
>>> assert result_to_maybe(Success(1)) == Some(1)
>>> assert result_to_maybe(Success(None)) == Some(None)
>>> assert result_to_maybe(Failure(1)) == Nothing
>>> assert result_to_maybe(Failure(None)) == Nothing
Converts Maybe
container to Result
container.
>>> from returns.maybe import Some, Nothing
>>> from returns.result import Failure, Success
>>> assert maybe_to_result(Some(1)) == Success(1)
>>> assert maybe_to_result(Some(None)) == Success(None)
>>> assert maybe_to_result(Nothing) == Failure(None)
This module provides a bunch of primitives to work with containers.
It makes composing functions with containers easier. Sometimes using methods on containers is not very helpful. Container methods are difficult to compose with other functions or methods.
Instead we can use functions that produce the same result but have the reverse semantics.
Usually, this means changing something like x.f(y)
to f(x)(y)
.
Why would anyone need these functions when you can use methods? To create pipelines!
from returns.pipeline import pipe
from returns.result import ResultE
def returns_result(arg: int) -> ResultE[int]:
...
def works_with_result(arg: int) -> ResultE[int]:
...
def finish_work(arg: int) -> ResultE[int]:
...
pipe(
returns_result,
works_with_result, # does not compose! Needs a container for input
finish_work, # does not compose either!
)
Without pointfree functions you would probably have to write:
returns_result().bind(works_with_result).bind(notifies_user)
And you need a way to somehow do this in the pipeline syntax. Remember that pipeline syntax helps make composing functions more readable and pythonic. That’s where pointfree functions become really useful.
map_()
is a pointfree alternative to the container method .map()
.
It lifts a function to work from container to container. map_(f)
would return f lifted to work on a container.
In other words, it modifies the function’s signature from:
a -> b
to:
Container[a] -> Container[b]
Doing this lets us compose regular functions and containers.
>>> from returns.pointfree import map_
>>> from returns.maybe import Maybe, Some
>>> def as_int(arg: str) -> int:
... return ord(arg)
>>> container: Maybe[str] = Some('a')
>>> # We now have two ways to compose container and as_int
>>> # 1. Via ``.map()``:
>>> assert container.map(as_int) == Some(97)
>>> # 2. Or via ``map_()``, like above but in the reverse order:
>>> assert map_(as_int)(container) == Some(97)
This means we can compose functions in a pipeline.
>>> from returns.pointfree import map_
>>> from returns.pipeline import flow
>>> from returns.maybe import Maybe, Some, Nothing
>>> def index_of_7(arg: str) -> Maybe[int]:
... if '7' in arg:
... return Some(arg.index('7'))
... return Nothing
>>> def double(num: int) -> int:
... return num * 2
>>> assert flow(
... '007',
... index_of_7, # Some(2)
... map_(double), # Some(4)
... ) == Some(4)
>>> # Still passes along Nothing
>>> assert flow(
... '006',
... index_of_7, # Nothing
... map_(double), # Nothing
... ) == Nothing
Pointfree bind()
is an alternative to the container method .bind()
.
It binds a function that returns a container so that is accepts the same container type as input.
In other words, it modifies the function’s signature from:
a -> Container[b]
to:
Container[a] -> Container[b]
Without bind()
it would be very hard to declaratively compose two entities:
Existing containers
Existing functions that accept a regular value and return a container
We can compose these entities with .bind()
when calling it on a container,
but how can we do it independently?
>>> from returns.pointfree import bind
>>> from returns.maybe import Maybe, Some
>>> def index_of_1(arg: str) -> Maybe[int]:
... if '1' in arg:
... return Some(arg.index('1'))
... return Nothing
>>> container = Some('A1 Steak Sauce')
>>> # We now have two way of composing these entities.
>>> # 1. Via ``.bind``:
>>> assert container.bind(index_of_1) == Some(1)
>>> # 2. Or via the ``bind`` function.
>>> assert bind(index_of_1)(container) == Some(1)
>>> # This produces the same result, but in a different order
That’s it!
We also have a long list of other bind_*
functions, like:
bind_io
to bind functions returning IO
container
bind_result
to bind functions returning Result
container
bind_ioresult
to bind functions returning IOResult
container
bind_future
to bind functions returning Future
container
bind_async_future
to bind async functions returning Future
container
bind_future_result
to bind functions returning FutureResult
container
bind_async_future_result
to bind async functions
returning FutureResult
container
bind_context
to bind functions returning RequiresContext
container
bind_context_result
to bind functions
returning RequiresContextResult
container
bind_context_ioresult
to bind functions
returning RequiresContextIOResult
container
bind_async
to bind async functions
returning Future
or FutureResult
bind_awaitable
to bind async non-container functions
Pointfree alt()
is an alternative to the container method .alt()
.
It lifts a function to act on the error contents of a container.
In other words, it modifies the function’s signature from:
a -> b
to:
Container[_, a] -> Container[_, b]
You can think of it like map
, but for the second type of a container.
>>> from returns.io import IOFailure, IOSuccess
>>> from returns.pointfree import alt
>>> def half_as_bad(error_code: int) -> float:
... return error_code / 2
>>> # When acting on a successful state, nothing happens.
>>> assert alt(half_as_bad)(IOSuccess(1)) == IOSuccess(1)
>>> # When acting on a failed state, the result changes
>>> assert alt(half_as_bad)(IOFailure(4)) == IOFailure(2.0)
>>> # This is equivalent to IOFailure(4).alt(half_as_bad)
>>> assert alt(half_as_bad)(IOFailure(4)) == IOFailure(4).alt(half_as_bad)
This inverse syntax lets us easily compose functions in a pipeline
>>> from returns.io import IOFailure, IOSuccess, IOResult
>>> from returns.pointfree import alt
>>> def always_errors(user_input: str) -> IOResult:
... return IOFailure(len(user_input))
>>> def twice_as_bad(exit_code: int) -> int:
... return exit_code * 2
>>> def make_error_message(exit_code: int) -> str:
... return f'Badness level: {exit_code}'
>>> assert flow(
... '12345',
... always_errors,
... alt(twice_as_bad),
... alt(make_error_message)
... ) == IOFailure('Badness level: 10')
Pointfree lash()
function is an alternative
to .lash()
container method.
It allows better composition by lifting a function that returns a container to act on the failed state of a container.
You can think of it like bind
, but for the second type of a container.
>>> from returns.pointfree import lash
>>> from returns.result import Success, Failure, Result
>>> def always_succeeds(arg: str) -> Result[int, str]:
... return Success(1)
>>> failed: Result[int, str] = Failure('a')
>>> # We now have two way of composing these entities.
>>> # 1. Via ``.lash``:
>>> assert failed.lash(always_succeeds) == Success(1)
>>> # 2. Or via ``lash`` function, the same but in the inverse way:
>>> assert lash(always_succeeds)(failed) == Success(1)
Pointfree apply
function allows
to use .apply()
container method like a function:
>>> from returns.pointfree import apply
>>> from returns.maybe import Some, Nothing
>>> def wow(arg: int) -> str:
... return chr(arg) + '!'
>>> assert apply(Some(wow))(Some(97)) == Some('a!')
>>> assert apply(Some(wow))(Some(98)) == Some('b!')
>>> assert apply(Some(wow))(Nothing) == Nothing
>>> assert apply(Nothing)(Nothing) == Nothing
If you wish to use apply
inside a pipeline
here’s how it might look:
>>> from returns.pointfree import apply
>>> from returns.pipeline import flow
>>> from returns.maybe import Some, Nothing, Maybe
>>> from typing import Callable
>>> def wow(arg: int) -> str:
... return chr(arg) + '!'
>>> def my_response(is_excited: bool) -> Maybe[Callable[[int], str]]:
... if is_excited:
... return Some(wow)
... return Nothing
>>> assert flow(
... Some(97),
... apply(my_response(True)),
... ) == Some('a!')
>>> assert flow(
... Nothing,
... apply(my_response(False)),
... ) == Nothing
Or with a function as the first parameter:
>>> from returns.pipeline import flow
>>> from returns.curry import curry
>>> from returns.maybe import Some
>>> @curry
... def add_curried(first: int, second: int) -> int:
... return first + second
>>> assert flow(
... Some(add_curried),
... Some(2).apply,
... Some(3).apply,
... ) == Some(5)
Sometimes we need to manipulate the inner Result
of some containers like
IOResult
or FutureResult
. With compose_result
we can do this
kind of manipulation.
>>> from returns.pointfree import compose_result
>>> from returns.io import IOResult, IOSuccess, IOFailure
>>> from returns.result import Result
>>> def cast_to_str(container: Result[float, int]) -> IOResult[str, int]:
... return IOResult.from_result(container.map(str))
>>> assert compose_result(cast_to_str)(IOSuccess(42.0)) == IOSuccess('42.0')
>>> assert compose_result(cast_to_str)(IOFailure(1)) == IOFailure(1)
Sometimes we need to create SingleFailableN
or DiverseFailableN
containers (e.g. Maybe
, ResultLikeN
) based on a boolean expression,
cond
can help us.
Consider cond
to be a functional if
.
See the example below:
>>> from returns.pipeline import flow
>>> from returns.pointfree import cond
>>> from returns.result import Result, Failure, Success
>>> def returns_boolean(arg: int) -> bool:
... return bool(arg)
>>> assert flow(
... returns_boolean(1),
... cond(Result, 'success', 'failure')
... ) == Success('success')
>>> assert flow(
... returns_boolean(0),
... cond(Result, 'success', 'failure')
... ) == Failure('failure')
Example using cond
with the Maybe
container:
>>> from returns.pipeline import flow
>>> from returns.pointfree import cond
>>> from returns.maybe import Maybe, Some, Nothing
>>> assert flow(
... returns_boolean(1),
... cond(Maybe, 'success')
... ) == Some('success')
>>> assert flow(
... returns_boolean(0),
... cond(Maybe, 'success')
... ) == Nothing
Lifts function to be wrapped in a container for better composition.
In other words, it modifies the function’s
signature from:
a -> b
to: Container[a] -> Container[b]
This is how it should be used:
>>> from returns.io import IO
>>> from returns.pointfree import map_
>>> def example(argument: int) -> float:
... return argument / 2
>>> assert map_(example)(IO(1)) == IO(0.5)
Note, that this function works for all containers with .map
method.
See returns.primitives.interfaces.mappable.MappableN
for more info.
See also
function (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_UpdatedType
)]) –
Kinded
[Callable
[[KindN
[TypeVar
(_MappableKind
, bound= MappableN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_MappableKind
, bound= MappableN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Turns function’s input parameter from a regular value to a container.
In other words, it modifies the function
signature from:
a -> Container[b]
to:
Container[a] -> Container[b]
Similar to returns.pointfree.lash()
,
but works for successful containers.
This is how it should be used:
>>> from returns.pointfree import bind
>>> from returns.maybe import Maybe, Some, Nothing
>>> def example(argument: int) -> Maybe[int]:
... return Some(argument + 1)
>>> assert bind(example)(Some(1)) == Some(2)
>>> assert bind(example)(Nothing) == Nothing
Note, that this function works for all containers with .bind
method.
See returns.primitives.interfaces.bindable.BindableN
for more info.
function (Callable
[[TypeVar
(_FirstType
)], KindN
[TypeVar
(_BindableKind
, bound= BindableN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_BindableKind
, bound= BindableN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_BindableKind
, bound= BindableN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Composes successful container with a function that returns a container.
In other words, it modifies the function’s
signature from:
a -> Result[b, c]
to:
Container[a, c] -> Container[b, c]
>>> from returns.io import IOSuccess
>>> from returns.context import RequiresContextResult
>>> from returns.result import Result, Success
>>> from returns.pointfree import bind_result
>>> def returns_result(arg: int) -> Result[int, str]:
... return Success(arg + 1)
>>> bound = bind_result(returns_result)
>>> assert bound(IOSuccess(1)) == IOSuccess(2)
>>> assert bound(RequiresContextResult.from_value(1))(...) == Success(2)
function (Callable
[[TypeVar
(_FirstType
)], Result
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_ResultLikeKind
, bound= ResultLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_ResultLikeKind
, bound= ResultLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Composes successful container with a function that returns a container.
In other words, it modifies the function’s
signature from:
a -> IO[b]
to:
Container[a, c] -> Container[b, c]
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.io import IO
>>> from returns.pointfree import bind_io
>>> def returns_io(arg: int) -> IO[int]:
... return IO(arg + 1)
>>> bound = bind_io(returns_io)
>>> assert bound(IO(1)) == IO(2)
>>> assert bound(IOSuccess(1)) == IOSuccess(2)
>>> assert bound(IOFailure(1)) == IOFailure(1)
function (Callable
[[TypeVar
(_FirstType
, contravariant=True)], IO
[TypeVar
(_UpdatedType
, covariant=True)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_IOLikeKind
, bound= IOLikeN
), TypeVar
(_FirstType
, contravariant=True), TypeVar
(_SecondType
), TypeVar
(_ThirdType
, contravariant=True)]], KindN
[TypeVar
(_IOLikeKind
, bound= IOLikeN
), TypeVar
(_UpdatedType
, covariant=True), TypeVar
(_SecondType
), TypeVar
(_ThirdType
, contravariant=True)]]]
Composes successful container with a function that returns a container.
In other words, it modifies the function’s
signature from:
a -> IOResult[b, c]
to:
Container[a, c] -> Container[b, c]
>>> from returns.io import IOResult, IOSuccess
>>> from returns.context import RequiresContextIOResult
>>> from returns.pointfree import bind_ioresult
>>> def returns_ioresult(arg: int) -> IOResult[int, str]:
... return IOSuccess(arg + 1)
>>> bound = bind_ioresult(returns_ioresult)
>>> assert bound(IOSuccess(1)) == IOSuccess(2)
>>> assert bound(
... RequiresContextIOResult.from_value(1),
... )(...) == IOSuccess(2)
function (Callable
[[TypeVar
(_FirstType
)], IOResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_IOResultLikeKind
, bound= IOResultLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_IOResultLikeKind
, bound= IOResultLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Compose a container and sync function returning Future
.
In other words, it modifies the function
signature from:
a -> Future[b]
to:
Container[a] -> Container[b]
Similar to returns.pointfree.lash()
,
but works for successful containers.
This is how it should be used:
>>> import anyio
>>> from returns.pointfree import bind_future
>>> from returns.future import Future
>>> from returns.io import IO
>>> def example(argument: int) -> Future[int]:
... return Future.from_value(argument + 1)
>>> assert anyio.run(
... bind_future(example)(Future.from_value(1)).awaitable,
... ) == IO(2)
Note, that this function works
for all containers with .bind_future
method.
See returns.primitives.interfaces.specific.future.FutureLikeN
for more info.
function (Callable
[[TypeVar
(_FirstType
)], Future
[TypeVar
(_UpdatedType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_FutureKind
, bound= FutureLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_FutureKind
, bound= FutureLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Compose a container and async function returning Future
.
In other words, it modifies the function
signature from:
a -> Awaitable[Future[b]]
to:
Container[a] -> Container[b]
This is how it should be used:
>>> import anyio
>>> from returns.pointfree import bind_async_future
>>> from returns.future import Future
>>> from returns.io import IO
>>> async def example(argument: int) -> Future[int]:
... return Future.from_value(argument + 1)
>>> assert anyio.run(
... bind_async_future(example)(Future.from_value(1)).awaitable,
... ) == IO(2)
Note, that this function works
for all containers with .bind_async_future
method.
See returns.primitives.interfaces.specific.future.FutureLikeN
for more info.
function (Callable
[[TypeVar
(_FirstType
)], Awaitable
[Future
[TypeVar
(_UpdatedType
)]]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_FutureKind
, bound= FutureLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_FutureKind
, bound= FutureLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Compose a container and async function returning FutureResult
.
In other words, it modifies the function
signature from:
a -> FutureResult[b, c]
to:
Container[a, c] -> Container[b, c]
This is how it should be used:
>>> import anyio
>>> from returns.pointfree import bind_future_result
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> def example(argument: int) -> FutureResult[int, str]:
... return FutureResult.from_value(argument + 1)
>>> assert anyio.run(
... bind_future_result(example)(
... FutureResult.from_value(1),
... ).awaitable,
... ) == IOSuccess(2)
>>> assert anyio.run(
... bind_future_result(example)(
... FutureResult.from_failure('a'),
... ).awaitable,
... ) == IOFailure('a')
Note, that this function works
for all containers with .bind_async_future
method.
See FutureResultLikeN
for more info.
function (Callable
[[TypeVar
(_FirstType
)], FutureResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_FutureResultKind
, bound= FutureResultLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_FutureResultKind
, bound= FutureResultLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Compose a container and async function returning FutureResult
.
In other words, it modifies the function
signature from:
a -> Awaitable[FutureResult[b, c]]
to:
Container[a, c] -> Container[b, c]
This is how it should be used:
>>> import anyio
>>> from returns.pointfree import bind_async_future_result
>>> from returns.future import FutureResult
>>> from returns.io import IOSuccess, IOFailure
>>> async def example(argument: int) -> FutureResult[int, str]:
... return FutureResult.from_value(argument + 1)
>>> assert anyio.run(
... bind_async_future_result(example)(
... FutureResult.from_value(1),
... ).awaitable,
... ) == IOSuccess(2)
>>> assert anyio.run(
... bind_async_future_result(example)(
... FutureResult.from_failure('a'),
... ).awaitable,
... ) == IOFailure('a')
Note, that this function works
for all containers with .bind_async_future
method.
See FutureResultLikeN
for more info.
function (Callable
[[TypeVar
(_FirstType
)], Awaitable
[FutureResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
)]]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_FutureResultKind
, bound= FutureResultLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_FutureResultKind
, bound= FutureResultLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Composes successful container with a function that returns a container.
In other words, it modifies the function’s
signature from:
a -> RequresContext[b, c]
to:
Container[a, c] -> Container[b, c]
>>> from returns.pointfree import bind_context2
>>> from returns.context import Reader
>>> def example(argument: int) -> Reader[int, int]:
... return Reader(lambda deps: argument + deps)
>>> assert bind_context2(example)(Reader.from_value(2))(3) == 5
Note, that this function works with only Kind2
containers
with .bind_context
method.
See returns.primitives.interfaces.specific.reader.ReaderLike2
for more info.
function (Callable
[[TypeVar
(_FirstType
)], RequiresContext
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_Reader2Kind
, bound= ReaderLike2
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), Any
]], KindN
[TypeVar
(_Reader2Kind
, bound= ReaderLike2
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), Any
]]]
Composes successful container with a function that returns a container.
In other words, it modifies the function’s
signature from: a -> RequresContext[b, c]
to: Container[a, c] -> Container[b, c]
>>> from returns.context import RequiresContext, RequiresContextResult
>>> from returns.result import Success, Failure
>>> from returns.pointfree import bind_context
>>> def function(arg: int) -> RequiresContext[str, int]:
... return RequiresContext(lambda deps: len(deps) + arg)
>>> assert bind_context(function)(
... RequiresContextResult.from_value(2),
... )('abc') == Success(5)
>>> assert bind_context(function)(
... RequiresContextResult.from_failure(0),
... )('abc') == Failure(0)
Note, that this function works with only Kind3
containers
with .bind_context
method.
See returns.primitives.interfaces.specific.reader.ReaderLike3
for more info.
function (Callable
[[TypeVar
(_FirstType
)], RequiresContext
[TypeVar
(_UpdatedType
), TypeVar
(_ThirdType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_Reader3Kind
, bound= ReaderLike3
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_Reader3Kind
, bound= ReaderLike3
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Useful alias for bind_context3()
.
function (Callable
[[TypeVar
(_FirstType
)], RequiresContext
[TypeVar
(_UpdatedType
), TypeVar
(_ThirdType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_Reader3Kind
, bound= ReaderLike3
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_Reader3Kind
, bound= ReaderLike3
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Modifies the second type argument of a ReaderLike2
.
In other words, it modifies the function’s
signature from:
a -> b
to:
Container[x, a] -> Container[x, b]
>>> from returns.pointfree import modify_env2
>>> from returns.context import RequiresContext
>>> def multiply(arg: int) -> RequiresContext[int, int]:
... return RequiresContext(lambda deps: arg * deps)
>>> assert modify_env2(int)(multiply(3))('4') == 12
Note, that this function works with only Kind2
containers
with .modify_env
method.
See returns.primitives.interfaces.specific.reader.ReaderLike2
for more info.
function (Callable
[[TypeVar
(_UpdatedType
)], TypeVar
(_SecondType
)]) –
Kinded
[Callable
[[KindN
[TypeVar
(_Reader2Kind
, bound= ReaderLike2
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), Any
]], KindN
[TypeVar
(_Reader2Kind
, bound= ReaderLike2
), TypeVar
(_FirstType
), TypeVar
(_UpdatedType
), Any
]]]
Modifies the third type argument of a ReaderLike3
.
In other words, it modifies the function’s
signature from: a -> b
to: Container[x, a] -> Container[x, b]
>>> from returns.pointfree import modify_env
>>> from returns.context import RequiresContextResultE
>>> from returns.result import Success, safe
>>> def divide(arg: int) -> RequiresContextResultE[float, int]:
... return RequiresContextResultE(safe(lambda deps: arg / deps))
>>> assert modify_env(int)(divide(3))('2') == Success(1.5)
>>> assert modify_env(int)(divide(3))('0').failure()
Note, that this function works with only Kind3
containers
with .modify_env
method.
See returns.primitives.interfaces.specific.reader.ReaderLike3
for more info.
function (Callable
[[TypeVar
(_UpdatedType
)], TypeVar
(_ThirdType
)]) –
Kinded
[Callable
[[KindN
[TypeVar
(_Reader3Kind
, bound= ReaderLike3
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_Reader3Kind
, bound= ReaderLike3
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_UpdatedType
)]]]
Useful alias for modify_env3()
.
function (Callable
[[TypeVar
(_UpdatedType
)], TypeVar
(_ThirdType
)]) –
Kinded
[Callable
[[KindN
[TypeVar
(_Reader3Kind
, bound= ReaderLike3
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_Reader3Kind
, bound= ReaderLike3
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_UpdatedType
)]]]
Composes successful container with a function that returns a container.
In other words, it modifies the function’s
signature from:
a -> ReaderResult[b, c, e]
to:
Container[a, c, e] -> Container[b, c, e]
>>> from returns.pointfree import bind_context_result
>>> from returns.context import ReaderIOResult, ReaderResult
>>> from returns.io import IOSuccess, IOFailure
>>> def example(argument: int) -> ReaderResult[int, str, str]:
... return ReaderResult.from_value(argument + 1)
>>> assert bind_context_result(example)(
... ReaderIOResult.from_value(1),
... )(...) == IOSuccess(2)
>>> assert bind_context_result(example)(
... ReaderIOResult.from_failure('a'),
... )(...) == IOFailure('a')
function (Callable
[[TypeVar
(_FirstType
)], RequiresContextResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_ReaderResultLikeKind
, bound= ReaderResultLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_ReaderResultLikeKind
, bound= ReaderResultLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Lifts function from RequiresContextIOResult
for better composition.
In other words, it modifies the function’s
signature from:
a -> RequiresContextIOResult[env, b, c]
to:
Container[env, a, c]
-> Container[env, b, c]
>>> import anyio
>>> from returns.context import (
... RequiresContextFutureResult,
... RequiresContextIOResult,
... )
>>> from returns.io import IOSuccess, IOFailure
>>> from returns.pointfree import bind_context_ioresult
>>> def function(arg: int) -> RequiresContextIOResult[str, int, str]:
... return RequiresContextIOResult(
... lambda deps: IOSuccess(len(deps) + arg),
... )
>>> assert anyio.run(bind_context_ioresult(function)(
... RequiresContextFutureResult.from_value(2),
... )('abc').awaitable) == IOSuccess(5)
>>> assert anyio.run(bind_context_ioresult(function)(
... RequiresContextFutureResult.from_failure(0),
... )('abc').awaitable) == IOFailure(0)
function (Callable
[[TypeVar
(_FirstType
)], RequiresContextIOResult
[TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_ReaderIOResultLikeKind
, bound= ReaderIOResultLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_ReaderIOResultLikeKind
, bound= ReaderIOResultLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Compose a container and async
function returning a container.
In other words, it modifies the function’s
signature from:
a -> Awaitable[Container[b]]
to:
Container[a] -> Container[b]
This is how it should be used:
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> from returns.pointfree import bind_async
>>> async def coroutine(x: int) -> Future[str]:
... return Future.from_value(str(x + 1))
>>> bound = bind_async(coroutine)(Future.from_value(1))
>>> assert anyio.run(bound.awaitable) == IO('2')
Note, that this function works
for all containers with .bind_async
method.
See returns.primitives.interfaces.specific.future.FutureLikeN
for more info.
function (Callable
[[TypeVar
(_FirstType
)], Awaitable
[KindN
[TypeVar
(_FutureKind
, bound= FutureLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_FutureKind
, bound= FutureLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_FutureKind
, bound= FutureLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Composes a container a regular async
function.
This function should return plain, non-container value.
In other words, it modifies the function’s
signature from:
a -> Awaitable[b]
to:
Container[a] -> Container[b]
This is how it should be used:
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> from returns.pointfree import bind_awaitable
>>> async def coroutine(x: int) -> int:
... return x + 1
>>> assert anyio.run(
... bind_awaitable(coroutine)(Future.from_value(1)).awaitable,
... ) == IO(2)
Note, that this function works
for all containers with .bind_awaitable
method.
See returns.primitives.interfaces.specific.future.FutureLikeN
for more info.
function (Callable
[[TypeVar
(_FirstType
)], Awaitable
[TypeVar
(_UpdatedType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_FutureKind
, bound= FutureLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_FutureKind
, bound= FutureLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Binds a function returning optional value over a container.
In other words, it modifies the function’s
signature from:
a -> Optional[b]
to:
Container[a] -> Container[b]
>>> from typing import Optional
>>> from returns.pointfree import bind_optional
>>> from returns.maybe import Some, Nothing
>>> def example(argument: int) -> Optional[int]:
... return argument + 1 if argument > 0 else None
>>> assert bind_optional(example)(Some(1)) == Some(2)
>>> assert bind_optional(example)(Some(0)) == Nothing
>>> assert bind_optional(example)(Nothing) == Nothing
Note, that this function works
for all containers with .bind_optional
method.
See returns.primitives.interfaces.specific.maybe._MaybeLikeKind
for more info.
function (Callable
[[TypeVar
(_FirstType
)], Optional
[TypeVar
(_UpdatedType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_MaybeLikeKind
, bound= MaybeLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_MaybeLikeKind
, bound= MaybeLikeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Composes inner Result
with IOResultLike
returning function.
Can be useful when you need an access to both states of the result.
>>> from returns.io import IOResult, IOSuccess, IOFailure
>>> from returns.pointfree import compose_result
>>> from returns.result import Result
>>> def modify_string(container: Result[str, str]) -> IOResult[str, str]:
... return IOResult.from_result(
... container.map(str.upper).alt(str.lower),
... )
>>> assert compose_result(modify_string)(
... IOSuccess('success')
... ) == IOSuccess('SUCCESS')
>>> assert compose_result(modify_string)(
... IOFailure('FAILURE')
... ) == IOFailure('failure')
function (Callable
[[Result
[TypeVar
(_FirstType
), TypeVar
(_SecondType
)]], KindN
[TypeVar
(_IOResultLikeKind
, bound= IOResultLikeN
), TypeVar
(_NewFirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_IOResultLikeKind
, bound= IOResultLikeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_IOResultLikeKind
, bound= IOResultLikeN
), TypeVar
(_NewFirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
Reduce the boilerplate when choosing paths.
Works with SingleFailableN
(e.g. Maybe
)
and DiverseFailableN
(e.g. Result
).
Example using cond
with the Result
container:
>>> from returns.pointfree import cond
>>> from returns.result import Failure, Result, Success
>>> assert cond(Result, 'success', 'failure')(True) == Success('success')
>>> assert cond(Result, 'success', 'failure')(False) == Failure('failure')
Example using cond
with the Maybe
container:
>>> from returns.maybe import Maybe, Some, Nothing
>>> assert cond(Maybe, 10.0)(True) == Some(10.0)
>>> assert cond(Maybe, 10.0)(False) == Nothing
container_type (Union
[Type
[TypeVar
(_SingleFailableKind
, bound= SingleFailableN
)], Type
[TypeVar
(_DiverseFailableKind
, bound= DiverseFailableN
)]]) –
success_value (TypeVar
(_ValueType
)) –
error_value (Optional
[TypeVar
(_ErrorType
)]) –
Lifts function to be wrapped in a container for better composition.
In other words, it modifies the function’s
signature from:
a -> b
to:
Container[a] -> Container[b]
This is how it should be used:
>>> from returns.io import IOFailure, IOSuccess
>>> from returns.pointfree import alt
>>> def example(argument: int) -> float:
... return argument / 2
>>> assert alt(example)(IOSuccess(1)) == IOSuccess(1)
>>> assert alt(example)(IOFailure(4)) == IOFailure(2.0)
Note, that this function works for all containers with .alt
method.
See returns.primitives.interfaces.altable.AltableN
for more info.
function (Callable
[[TypeVar
(_SecondType
)], TypeVar
(_UpdatedType
)]) –
Kinded
[Callable
[[KindN
[TypeVar
(_AltableKind
, bound= AltableN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_AltableKind
, bound= AltableN
), TypeVar
(_FirstType
), TypeVar
(_UpdatedType
), TypeVar
(_ThirdType
)]]]
Turns function’s input parameter from a regular value to a container.
In other words, it modifies the function
signature from:
a -> Container[b]
to:
Container[a] -> Container[b]
Similar to returns.pointfree.bind()
, but works for failed containers.
This is how it should be used:
>>> from returns.pointfree import lash
>>> from returns.result import Success, Failure, Result
>>> def example(argument: int) -> Result[str, int]:
... return Success(argument + 1)
>>> assert lash(example)(Success('a')) == Success('a')
>>> assert lash(example)(Failure(1)) == Success(2)
Note, that this function works for all containers with .lash
method.
See returns.interfaces.lashable.Lashable
for more info.
function (Callable
[[TypeVar
(_SecondType
)], KindN
[TypeVar
(_LashableKind
, bound= LashableN
), TypeVar
(_FirstType
), TypeVar
(_UpdatedType
), TypeVar
(_ThirdType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_LashableKind
, bound= LashableN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_LashableKind
, bound= LashableN
), TypeVar
(_FirstType
), TypeVar
(_UpdatedType
), TypeVar
(_ThirdType
)]]]
Composes successful container with a function that returns a container.
Similar to bind()
but has different type.
It returns Result[ValueType, Union[OldErrorType, NewErrorType]]
instead of Result[ValueType, OldErrorType]
.
So, it can be more useful in some situations. Probably with specific exceptions.
>>> from returns.methods import cond
>>> from returns.pointfree import unify
>>> from returns.result import Result, Success, Failure
>>> def bindable(arg: int) -> Result[int, int]:
... return cond(Result, arg % 2 == 0, arg + 1, arg - 1)
>>> assert unify(bindable)(Success(2)) == Success(3)
>>> assert unify(bindable)(Success(1)) == Failure(0)
>>> assert unify(bindable)(Failure(42)) == Failure(42)
function (Callable
[[TypeVar
(_FirstType
)], KindN
[TypeVar
(_DiverseFailableKind
, bound= DiverseFailableN
), TypeVar
(_NewFirstType
), TypeVar
(_NewSecondType
), TypeVar
(_NewThirdType
)]]) –
Kinded
[Callable
[[KindN
[TypeVar
(_DiverseFailableKind
, bound= DiverseFailableN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_DiverseFailableKind
, bound= DiverseFailableN
), TypeVar
(_NewFirstType
), Union
[TypeVar
(_SecondType
), TypeVar
(_NewSecondType
)], TypeVar
(_NewThirdType
)]]]
Turns container containing a function into a callable.
In other words, it modifies the function
signature from:
Container[a -> b]
to:
Container[a] -> Container[b]
This is how it should be used:
>>> from returns.pointfree import apply
>>> from returns.maybe import Some, Nothing
>>> def example(argument: int) -> int:
... return argument + 1
>>> assert apply(Some(example))(Some(1)) == Some(2)
>>> assert apply(Some(example))(Nothing) == Nothing
>>> assert apply(Nothing)(Some(1)) == Nothing
>>> assert apply(Nothing)(Nothing) == Nothing
Note, that this function works for all containers with .apply
method.
See returns.interfaces.applicative.ApplicativeN
for more info.
container (KindN
[TypeVar
(_ApplicativeKind
, bound= ApplicativeN
), Callable
[[TypeVar
(_FirstType
)], TypeVar
(_UpdatedType
)], TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
Kinded
[Callable
[[KindN
[TypeVar
(_ApplicativeKind
, bound= ApplicativeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]], KindN
[TypeVar
(_ApplicativeKind
, bound= ApplicativeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]]
The following useful methods can be used to interact with interfaces.
Reduce the boilerplate when choosing paths with DiverseFailableN
.
Think of this method as a functional if
alternative
for successful or failed types.
So, this code:
>>> from returns.result import Failure, Result, Success
>>> def is_numeric(string: str) -> Result[str, str]:
... if string.isnumeric():
... return Success('It is a number')
... return Failure('It is not a number')
Can be replaced with this:
>>> from returns.methods import cond
>>> from returns.result import Failure, Result, Success
>>> def is_numeric(string: str) -> Result[str, str]:
... return cond(
... Result,
... string.isnumeric(),
... 'It is a number',
... 'It is not a number',
... )
>>> assert is_numeric('42') == Success('It is a number')
>>> assert is_numeric('text') == Failure('It is not a number')
Why is it helpful? Because cond
can be easily added
into a Pipelines of functions.
Unwraps either a successful or failed value.
>>> from returns.io import IO, IOSuccess, IOFailure
>>> from returns.methods import unwrap_or_failure
>>> assert unwrap_or_failure(IOSuccess(1)) == IO(1)
>>> assert unwrap_or_failure(IOFailure('a')) == IO('a')
Useful when you have a ResultLike
value with correctly handled error value,
for example with bimap()
.
Here’s a full example:
>>> from returns.result import Failure, Result, Success
>>> from returns.methods import unwrap_or_failure
>>> from returns.pointfree import bimap
>>> instance: Result[int, str] = Success(1)
>>> error_handled = bimap(lambda inr: inr + 1, lambda _: 0)(instance)
>>> assert isinstance(unwrap_or_failure(error_handled), int)
Kinded version of internal_cond()
, use it to infer real return type.
container_type (Union
[Type
[TypeVar
(_SingleFailableKind
, bound= SingleFailableN
)], Type
[TypeVar
(_DiverseFailableKind
, bound= DiverseFailableN
)]]) –
is_success (bool
) –
success_value (TypeVar
(_ValueType
)) –
error_value (Optional
[TypeVar
(_ErrorType
)]) –
Unwraps either successful or failed value.
>>> from returns.io import IO, IOSuccess, IOFailure
>>> from returns.methods import unwrap_or_failure
>>> assert unwrap_or_failure(IOSuccess(1)) == IO(1)
>>> assert unwrap_or_failure(IOFailure('a')) == IO('a')
container (Unwrappable
[TypeVar
(_FirstType
), TypeVar
(_SecondType
)]) –
Union
[TypeVar
(_FirstType
), TypeVar
(_SecondType
)]
Note
Technical note: this feature requires mypy plugin.
All containers can be easily composed with functions that can take a single argument.
But, what if we need to compose two containers with a function with two arguments? That’s not so easy.
Of course, we can use Curry and .apply
or some imperative code.
But, it is not very easy to write and read.
This is why multiple functional languages have a concept of “do-notation”. It allows you to write beautiful imperative code.
Let’s say we have a function called add
which is defined like this:
>>> def add(one: int, two: int) -> int:
... return one + two
And we have two containers: IO(2)
and IO(3)
.
How can we easily get IO(5)
in this case?
Luckily, IO
defines returns.io.IO.do()
which can help us:
>>> from returns.io import IO
>>> assert IO.do(
... add(first, second)
... for first in IO(2)
... for second in IO(3)
... ) == IO(5)
Notice, that you don’t have two write any complicated code. Everything is pythonic and readable.
However, we still need to explain what for
does here.
It uses Python’s __iter__
method which returns an iterable
with strictly a single raw value inside.
Warning
Please, don’t use for x in container
outside of do-notation.
It does not make much sense.
Basically, for IO(2)
it will return just 2
.
Then, IO.do
wraps it into IO
once again.
Containers like Result
and IOResult
can sometimes represent errors.
In this case, do-notation expression will return the first found error.
For example:
>>> from returns.result import Success, Failure, Result
>>> assert Result.do(
... first + second
... for first in Failure('a')
... for second in Success(3)
... ) == Failure('a')
This behavior is consistent with .map
and other methods.
We also support async containers like Future
and FutureResult
.
It works in a similar way as regular sync containers.
But, they require async for
expressions instead of regular for
ones.
And because of that - they cannot be used outsided of async def
context.
Usage example:
>>> import anyio
>>> from returns.future import Future
>>> from returns.io import IO
>>> async def main() -> None:
... return await Future.do(
... first + second
... async for first in Future.from_value(1)
... async for second in Future.from_value(2)
... )
>>> assert anyio.run(main) == IO(3)
One might ask, why don’t we allow mixing multiple container types in a single do-notation expression?
For example, this code will not what you expect:
>>> from returns.result import Result, Success
>>> from returns.io import IOResult, IOSuccess
>>> assert Result.do(
... first + second
... for first in Success(2)
... for second in IOSuccess(3) # Notice the IO part here
... ) == Success(5)
This code will raise a mypy error at for second in IOSuccess(3)
part:
Invalid type supplied in do-notation: expected "returns.result.Result[Any, Any]", got "returns.io.IOSuccess[builtins.int*]"
Notice, that the IO
part is gone in the final result. This is not right.
And we can’t track this in any manner.
So, we require all containers to have the same type.
The code above must be rewritten as:
>>> from returns.result import Success
>>> from returns.io import IOResult, IOSuccess
>>> assert IOResult.do(
... first + second
... for first in IOResult.from_result(Success(2))
... for second in IOSuccess(3)
... ) == IOSuccess(5)
Now, it is correct. IO
part is safe, the final result is correct.
And mypy is happy.
if
conditions in generator expressions?¶At the moment, using if
conditions inside generator expressions
passed into .do
method is not allowed. Why?
Because if the if
condition will return False
,
we will have an empty iterable and StopIteration
will be thrown.
>>> from returns.io import IO
>>> IO.do(
... first + second
... for first in IO(2)
... for second in IO(3)
... if second > 10
... )
Traceback (most recent call last):
...
StopIteration
It will raise:
Using "if" conditions inside a generator is not allowed
Instead, use conditions and checks inside your logic, not inside your generator.
This code will work in runtime, but will raise a mypy error:
>>> from returns.result import Result, Success
>>> expr = (
... first + second
... for first in Success(2)
... for second in Success(3)
... )
>>>
>>> assert Result.do(expr) == Success(5)
It raises:
Literal generator expression is required, not a variable or function call
This happens, because of mypy’s plugin API. We need the whole expression to make sure it is correct. We cannot use variables and function calls in its place.
We feature several helper functions to make your developer experience better.
We also ship an utility function to compose two different functions together.
>>> from returns.functions import compose
>>> bool_after_int = compose(int, bool)
>>> assert bool_after_int('1') is True
>>> assert bool_after_int('0') is False
Composition is also type-safe. The only limitation is that we only support functions with one argument and one return to be composed.
Only works with regular functions (not async).
Warning
compose
might fail to infer types for some functions.
There are several problems: lambda
and generic functions.
In these cases mypy
will fail to infer
the types of the resulting function.
In this case, use pipe
it does the same thing, but has pretty good type inference.
Or use manual annotations with Callable
type.
We also ship returns.functions.identity()
function
to help you with the composition.
Identity function is a simple concept: it just returns its argument. If you wonder why do we need this function, please read below:
We need tap()
function to easily compose values
with functions that does not return.
For example you sometimes need to print()
values inside your pipe:
>>> from returns.functions import tap
>>> result = tap(print)(1) # will print and return 1
1
>>> assert result == 1
You can also use the untap
function to turn any function’s
return type to None
and still do its thing:
>>> from returns.functions import tap, untap
>>> result = untap(tap(print))(1) # will print and return None
1
>>> assert result is None
This is also sometimes helpful for a typed function composition.
Sometimes you really want to reraise an exception from Failure[Exception]
due to some existing API (or a dirty hack).
We allow you to do that with ease!
from returns.functions import raise_exception
def create_account_and_user(username: str) -> ...:
"""
Creates new Account-User pair.
Imagine, that you need to reraise ValidationErrors due to existing API.
"""
return _validate_user(
username,
).alt(
# What happens here is interesting, since you do not let your
# unwrap to fail with UnwrapFailedError, but instead
# allows you to reraise a wrapped exception.
# In this case `ValidationError()` will be thrown
# before `UnwrapFailedError`
raise_exception,
)
def _validate_user(username: str) -> Result['User', ValidationError]:
...
Use this with caution. We try to remove exceptions from our code base. Original proposal is here.
With not_
helper function we can easily deny a function returns.
It supports functions with one or more arguments.
>>> from typing import List
>>> from returns.functions import compose, not_
>>> def is_even(number: int) -> bool:
... return number % 2 == 0
>>> def number_is_in_list(number: int, list_: List[int]) -> bool:
... return number in list_
>>> assert not_(is_even)(2) is False
>>> assert not_(number_is_in_list)(1, [2, 3, 4]) is True
>>> assert compose(int, not_(is_even))("1") is True
Function that returns its argument.
>>> assert identity(1) == 1
>>> assert identity([1, 2, 3]) == [1, 2, 3]
This function is really helpful for some composition. It is also useful for “do nothing” use-case.
See also
instance (TypeVar
(_FirstType
)) –
TypeVar
(_FirstType
)
Allows function composition.
Works as: second . first
or first() |> second()
.
You can read it as “second after first”.
>>> assert compose(float, int)('123.5') == 123
We can only compose functions with one argument and one return. Type checked.
first (Callable
[[TypeVar
(_FirstType
)], TypeVar
(_SecondType
)]) –
second (Callable
[[TypeVar
(_SecondType
)], TypeVar
(_ThirdType
)]) –
Callable
[[TypeVar
(_FirstType
)], TypeVar
(_ThirdType
)]
Allows to apply some function and return an argument, instead of a result.
Is useful for composing functions with
side-effects like print()
, logger.log()
, etc.
>>> assert tap(print)(1) == 1
1
>>> assert tap(lambda _: 1)(2) == 2
function (Callable
[[TypeVar
(_FirstType
)], Any
]) –
Callable
[[TypeVar
(_FirstType
)], TypeVar
(_FirstType
)]
Allows to apply some function and always return None
as a result.
Is useful for composing functions that do some side effects and return some nosense.
Is the kind of a reverse of the tap
function.
>>> def strange_log(arg: int) -> int:
... print(arg)
... return arg
>>> assert untap(strange_log)(2) is None
2
>>> assert untap(tap(lambda _: 1))(2) is None
function (Callable
[[TypeVar
(_FirstType
)], Any
]) –
Callable
[[TypeVar
(_FirstType
)], None
]
Helper function to raise exceptions as a function.
It might be required as a compatibility tool for existing APIs. That’s how it can be used:
>>> from returns.result import Failure, Result
>>> # Some operation result:
>>> user: Result[int, ValueError] = Failure(ValueError('boom'))
>>> # Here we unwrap internal exception and raise it:
>>> user.alt(raise_exception)
Traceback (most recent call last):
...
ValueError: boom
exception (Exception
) –
NoReturn
Denies the function returns.
>>> from returns.result import Result, Success, Failure
>>> def is_successful(result_container: Result[float, int]) -> bool:
... return isinstance(result_container, Result.success_type)
>>> assert not_(is_successful)(Success(1.0)) is False
>>> assert not_(is_successful)(Failure(1)) is True
function (Callable
[[ParamSpec
(_FuncParams
)], bool
]) –
Callable
[[ParamSpec
(_FuncParams
)], bool
]
This module is dedicated to partial application.
We support two types of partial application: @curry
and partial
.
@curry
is a new concept for most Python developers,
but Python already has a great tool to use partial application:
functools.partial
The only problem with it is the lack of typing. Let’s see what problems do we solve with this module.
Warning
This module requires our mypy plugin to be present. Without it we will fallback to the original behaviour.
Here’s how typing works there:
from functools import partial
def some_function(first: int, second: int) -> float:
return first / second
reveal_type(partial(some_function, 1))
# => functools.partial[builtins.float*]
# => Which is really: `def (*Any, **Any) -> builtins.float`
And compare it with our solution:
from returns.curry import partial
def some_function(first: int, second: int) -> float:
return first / second
reveal_type(partial(some_function, 1))
# => def (second: builtins.int) -> builtins.float*
# => Which is fair!
Note
We still use functools.partial
inside.
We just improve the typings.
One more problem is generics support in functools.partial
.
Here’s the comparison:
from functools import partial
from typing import List, TypeVar
T = TypeVar('T')
x: List[int]
def some_function(first: List[T], second: int) -> T:
return first[second]
reveal_type(partial(some_function, x))
# => functools.partial[T`-1]
# => Which is broken!
And our solution works fine:
from returns.curry import partial
reveal_type(partial(some_function, x))
# => def (second: builtins.int) -> builtins.int*
We also work with complex generic with multiple arguments or with multiple generics.
The only known problem is that passing explicit generic like [1, 2, 3]
will resolve in List[Any]
. Because mypy
won’t be able to infer
this type for some reason.
The reasonable work-around is to pass annotated variables like in the example above.
We can also work with types and instances. Because they are callable too!
from returns.curry import partial
class Test(object):
def __init__(self, arg: int) -> None:
self.arg = arg
def __call__(self, other: int) -> int:
return self.arg + other
reveal_type(partial(Test, 1)) # N: Revealed type is 'def () -> ex.Test'
reveal_type(partial(Test(1), 1)) # N: Revealed type is 'def () -> builtins.int'
No differences with regular callables at all.
We also support working with @overload
definitions.
It also looks the same way:
from typing import overload
from returns.curry import partial
@overload
def test(a: int, b: str) -> str:
...
@overload
def test(a: int) -> int:
...
@overload
def test(a: str) -> None: # won't match!
...
def test(a, b=None):
...
reveal_type(partial(test, 1)) # N: Revealed type is 'Overload(def (b: builtins.str) -> builtins.str, def () -> builtins.int)'
From this return type you can see that we work with all matching cases and discriminate unmatching ones.
curry
allows to provide only a subset of arguments to a function.
And it won’t be called until all the required arguments are provided.
In contrast to partial
which works on the calling stage,
@curry
works best when defining a new function.
>>> from returns.curry import curry
>>> @curry
... def function(first: int, second: str) -> bool:
... return len(second) > first
>>> assert function(1)('a') is False
>>> assert function(1, 'a') is False
>>> assert function(2)('abc') is True
>>> assert function(2, 'abc') is True
Take a note, that providing invalid arguments will raise TypeError
:
>>> function(1, 2, 3)
Traceback (most recent call last):
...
TypeError: too many positional arguments
>>> function(a=1)
Traceback (most recent call last):
...
TypeError: got an unexpected keyword argument 'a'
This is really helpful when working with .apply()
method of containers.
Warning
We recommend using partial instead of @curry
when
possible because it’s much faster.
@curry
functions are also fully typed with our custom mypy
plugin.
Let’s see how types do look like for a curried function:
>>> from returns.curry import curry
>>> @curry
... def zero(a: int, b: float, *, kw: bool) -> str:
... return str(a - b) if kw else ''
>>> assert zero(1)(0.3)(kw=True) == '0.7'
>>> assert zero(1)(0.3, kw=False) == ''
# If we will reveal the type it would be quite big:
reveal_type(zero)
# Overload(
# def (a: builtins.int) -> Overload(
# def (b: builtins.float, *, kw: builtins.bool) -> builtins.str,
# def (b: builtins.float) -> def (*, kw: builtins.bool) -> builtins.str
# ),
# def (a: builtins.int, b: builtins.float) -> def (*, kw: builtins.bool)
# -> builtins.str,
# def (a: builtins.int, b: builtins.float, *, kw: builtins.bool)
# -> builtins.str
# )
It reveals to us that there are 4 possible way to call this function. And we type all of them with overload type.
When you provide any arguments, you discriminate some overloads and choose more specific path:
reveal_type(zero(1, 2.0))
# By providing this set of arguments we have chosen this path:
#
# def (a: builtins.int, b: builtins.float) -> def (*, kw: builtins.bool)
# -> builtins.str,
#
# And the revealed type would be:
#
# def (*, kw: builtins.bool) -> builtins.str
#
It works with functions, instance, class,
and static methods, including generics.
See Limitations
in the API Reference.
When you use partial(some, *my_args)
or partial(some, **my_args)
or both of them at the same time,
we fallback to the default return type. The same happens with curry
. Why?
There are several problems:
Because mypy
cannot not infer what arguments are there
inside this my_args
variable
Because curry
cannot know when
to stop accepting *args
and **kwargs
And there are possibly other problems!
Our advice is not to use *args
and *kwargs
with partial
and curry
.
But, it is still possible, but in this case we will fallback to Any
.
Typed partial application.
It is just a functools.partial
wrapper with better typing support.
We use a custom mypy
plugin to make sure types are correct.
Otherwise, it is currently impossible to properly type this function.
>>> from returns.curry import partial
>>> def sum_two_numbers(first: int, second: int) -> int:
... return first + second
>>> sum_with_ten = partial(sum_two_numbers, 10)
>>> assert sum_with_ten(2) == 12
>>> assert sum_with_ten(-5) == 5
func (Callable
[...
, TypeVar
(_ReturnType
)]) –
args (Any
) –
kwargs (Any
) –
Callable
[...
, TypeVar
(_ReturnType
)]
Typed currying decorator.
Currying is a conception from functional languages that does partial applying. That means that if we pass one argument in a function that gets 2 or more arguments, we’ll get a new function that remembers all previously passed arguments. Then we can pass remaining arguments, and the function will be executed.
partial()
function does a similar thing,
but it does partial application exactly once.
curry
is a bit smarter and will do partial
application until enough arguments passed.
If wrong arguments are passed, TypeError
will be raised immediately.
We use a custom mypy
plugin to make sure types are correct.
Otherwise, it is currently impossible to properly type this function.
>>> from returns.curry import curry
>>> @curry
... def divide(number: int, by: int) -> float:
... return number / by
>>> divide(1) # doesn't call the func and remembers arguments
<function divide at ...>
>>> assert divide(1)(by=10) == 0.1 # calls the func when possible
>>> assert divide(1)(10) == 0.1 # calls the func when possible
>>> assert divide(1, by=10) == 0.1 # or call the func like always
Here are several examples with wrong arguments:
>>> divide(1, 2, 3)
Traceback (most recent call last):
...
TypeError: too many positional arguments
>>> divide(a=1)
Traceback (most recent call last):
...
TypeError: got an unexpected keyword argument 'a'
Limitations:
It is kinda slow. Like 100 times slower than a regular function call.
It does not work with several builtins like str
, int
,
and possibly other C
defined callables
*args
and **kwargs
are not supported
and we use Any
as a fallback
Support of arguments with default values is very limited, because we cannot be totally sure which case we are using: with the default value or without it, be careful
We use a custom mypy
plugin to make types correct,
otherwise, it is currently impossible
It might not work as expected with curried Klass().method
,
it might generate invalid method signature
(looks like a bug in mypy
)
It is probably a bad idea to curry
a function with lots of arguments,
because you will end up with lots of overload functions,
that you won’t be able to understand.
It might also be slow during the typecheck
Currying of __init__
does not work because of the bug in mypy
:
https://github.com/python/mypy/issues/8801
We expect people to use this tool responsibly when they know that they are doing.
function (Callable
[...
, TypeVar
(_ReturnType
)]) –
Callable
[...
, TypeVar
(_ReturnType
)]
We have several utility types that we use for our containers, that can also help end users as well.
You can use all power of declarative loops in your app with Fold
.
>>> from returns.iterables import Fold
>>> from returns.io import IO
>>> items = [IO(1), IO(2), IO(3)]
>>> assert Fold.loop(
... items,
... IO(''),
... lambda num: lambda text: text + str(num),
... ) == IO('123')
There are also other helpful methods as well.
See returns.iterables.AbstractFold
.
We also ship AbstractFold
,
where you can change how loop
(or any other) method works.
For example, for performance reasons.
Let’s say you have a big number of
RequiresContext
instances
and you want to do the same thing string concatenation we have shown above.
You might face recursion problems with it:
>>> import sys
>>> from returns.context import Reader
>>> from returns.iterables import Fold
>>> items = [Reader.from_value(num) for num in range(sys.getrecursionlimit())]
>>> Fold.loop(items, Reader.from_value(0), lambda x: lambda y: x + y)(...)
Traceback (most recent call last):
...
RecursionError: ...
So, let’s change how it works for this specific type:
>>> from returns.iterables import AbstractFold
>>> class ContextAwareFold(AbstractFold):
... @classmethod
... def _loop(cls, iterable, acc, function, concat, deps=None):
... wrapped = acc.from_value(function)
... for current in iterable:
... assert isinstance(current, Reader)
... acc = Reader.from_value(concat(current, acc, wrapped)(deps))
... return acc
Note
Don’t forget to add typing annotations to your real code! This is just an example.
And now let’s test that it works without recursion:
>>> items = [Reader.from_value(num) for num in range(sys.getrecursionlimit())]
>>> assert ContextAwareFold.loop(
... items, Reader.from_value(0), lambda x: lambda y: x + y,
... )(...) == sum(range(sys.getrecursionlimit()))
And no error will be produced! We now don’t use recursion inside. Consider this way of doing things as a respected hack.
This class is useful when you need to make some instances immutable (like our containers are immutable).
Bases: object
A collection of different helpers to write declarative Iterable
actions.
Allows to work with iterables.
Implementation
AbstractFold
and Fold
types are special.
They have double definition for each method: public and protected ones.
Why?
Because you cannot override @kinded
method due to a mypy
bug.
So, there are two opportunities for us here:
Declare all method as @final
and do not allow to change anything
Use delegation to protected unkinded methods
We have chosen the second way! Here’s how it works:
Public methods are @kinded
for better typing and cannot be overridden
Protected methods are unkinded and can be overridden in subtyping
Now, if you need to make a change into our implementation,
then you can subclass Fold
or AbstractFold
and then
change an implementation of any unkinded protected method.
Allows to make declarative loops for any ApplicativeN
subtypes.
Quick example:
>>> from typing import Callable
>>> from returns.maybe import Some
>>> from returns.iterables import Fold
>>> def sum_two(first: int) -> Callable[[int], int]:
... return lambda second: first + second
>>> assert Fold.loop(
... [Some(1), Some(2), Some(3)],
... Some(10),
... sum_two,
... ) == Some(16)
Looks like foldl
in some other languages with some more specifics.
See: https://philipschwarz.dev/fpilluminated/?page_id=348#bwg3/137
Is also quite similar to reduce
.
Public interface for _loop
method. Cannot be modified directly.
iterable (Iterable
[KindN
[TypeVar
(_ApplicativeKind
, bound= ApplicativeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
acc (KindN
[TypeVar
(_ApplicativeKind
, bound= ApplicativeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
function (Callable
[[TypeVar
(_FirstType
)], Callable
[[TypeVar
(_UpdatedType
)], TypeVar
(_UpdatedType
)]]) –
KindN
[TypeVar
(_ApplicativeKind
, bound= ApplicativeN
), TypeVar
(_UpdatedType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Transforms an iterable of containers into a single container.
Quick example for regular containers:
>>> from returns.io import IO
>>> from returns.iterables import Fold
>>> items = [IO(1), IO(2)]
>>> assert Fold.collect(items, IO(())) == IO((1, 2))
If container can have failed values, then this strategy fails on any existing failed like type.
It is enough to have even a single failed value in iterable for this type to convert the whole operation result to be a failure. Let’s see how it works:
>>> from returns.result import Success, Failure
>>> from returns.iterables import Fold
>>> empty = []
>>> all_success = [Success(1), Success(2), Success(3)]
>>> has_failure = [Success(1), Failure('a'), Success(3)]
>>> all_failures = [Failure('a'), Failure('b')]
>>> acc = Success(()) # empty tuple
>>> assert Fold.collect(empty, acc) == Success(())
>>> assert Fold.collect(all_success, acc) == Success((1, 2, 3))
>>> assert Fold.collect(has_failure, acc) == Failure('a')
>>> assert Fold.collect(all_failures, acc) == Failure('a')
If that’s now what you need, check out
collect_all()
to force collect all non-failed values.
Public interface for _collect
method. Cannot be modified directly.
iterable (Iterable
[KindN
[TypeVar
(_ApplicativeKind
, bound= ApplicativeN
), TypeVar
(_FirstType
), TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]]) –
acc (KindN
[TypeVar
(_ApplicativeKind
, bound= ApplicativeN
), Tuple
[TypeVar
(_FirstType
), ...
], TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]) –
KindN
[TypeVar
(_ApplicativeKind
, bound= ApplicativeN
), Tuple
[TypeVar
(_FirstType
), ...
], TypeVar
(_SecondType
), TypeVar
(_ThirdType
)]
Transforms an iterable of containers into a single container.
This method only works with FailableN
subtypes,
not just any ApplicativeN
like collect()
.
Strategy to extract all successful values even if there are failed values.
If there’s at least one successful value and any amount of failed values, we will still return all collected successful values.
We can return failed value for this strategy only in a single case: when default element is a failed value.
Let’s see how it works:
>>> from returns.result import Success, Failure
>>> from returns.iterables import Fold
>>> empty = []
>>> all_success = [Success(1), Success(2), Success(3)]
>>> has_failure = [Success(1), Failure('a'), Success(3)]
>>> all_failures = [Failure('a'), Failure('b')]
>>> acc = Success(()) # empty tuple
>>> assert Fold.collect_all(empty, acc) == Success(())
>>> assert Fold.collect_all(all_success, acc) == Success((1, 2, 3))
>>> assert Fold.collect_all(has_failure, acc) == Success((1, 3))
>>> assert Fold.collect_all(all_failures, acc) == Success(())
>>> assert Fold.collect_all(empty, Failure('c')) == Failure('c')
If that’s now what you need, check out collect()
to collect only successful values and fail on any failed ones.
Public interface for _collect_all
method.
Cannot be modified directly.
Bases: AbstractFold
Concrete implementation of AbstractFold
of end users.
Use it by default.
Bases: object
Helper type for objects that should be immutable.
When applied, each instance becomes immutable. Nothing can be added or deleted from it.
>>> from returns.primitives.types import Immutable
>>> class MyModel(Immutable):
... ...
>>> model = MyModel()
>>> model.prop = 1
Traceback (most recent call last):
...
returns.primitives.exceptions.ImmutableStateError
See returns.primitives.container.BaseContainer
for examples.
Bases: Exception
Raised when a container can not be unwrapped into a meaningful value.
container (Unwrappable
) –
Bases: AttributeError
Raised when a container is forced to be mutated.
It is a sublclass of AttributeError
for two reasons:
It seems kinda reasonable to expect AttributeError
on attribute modification
It is used inside typing.py
this way,
we do have several typing features that requires that behaviour
Sometimes we want to trace where the Failure
has occurred in our system,
returns
provide a way to trace those failures.
By default tracing is disabled.
The trace is accessible by trace
property
that is available for Result
, IOResult
containers. It’s basically a list
containing all inspect.FrameInfo
objects from the call stack when the
Failure
was originally created.
To enable it you can use
collect_traces
.
See examples below:
You can use it as a context manager:
>>> from inspect import FrameInfo
>>> from returns.result import Failure, Result
>>> from returns.primitives.tracing import collect_traces
>>> def get_failure(argument: str) -> Result[str, str]:
... return Failure(argument)
>>> non_traced_failure = get_failure('Normal Failure')
>>> with collect_traces():
... traced_failure = get_failure('Traced Failure')
>>> assert non_traced_failure.trace is None
>>> assert isinstance(traced_failure.trace, list)
>>> assert all(isinstance(trace_line, FrameInfo) for trace_line in traced_failure.trace)
>>> for trace_line in traced_failure.trace:
... print(f"{trace_line.filename}:{trace_line.lineno} in `{trace_line.function}`")
...
/returns/returns/result.py:529 in `Failure`
/example_folder/example.py:5 in `get_failure`
/example_folder/example.py:1 in `<module>`
Or as a decorator:
>>> from inspect import FrameInfo
>>> from returns.io import IOFailure, IOResult
>>> from returns.result import Failure, Result
>>> from returns.primitives.tracing import collect_traces
>>> @collect_traces
... def traced_function(value: str) -> IOResult[str, str]:
... return IOFailure(value)
>>> non_traced_failure = Failure('Normal Failure')
>>> traced_failure = traced_function('Traced Failure')
>>> assert non_traced_failure.trace is None
>>> assert isinstance(traced_failure.trace, list)
>>> assert all(isinstance(trace_line, FrameInfo) for trace_line in traced_failure.trace)
>>> for trace_line in traced_failure.trace:
... print(f"{trace_line.filename}:{trace_line.lineno} in `{trace_line.function}`")
...
/returns/returns/result.py:525 in `Failure`
/returns/returns/io.py:852 in `IOFailure`
/example_folder/example.py:7: in `traced_function`
/usr/lib/python3.8/contextlib.py:75 in `inner`
/example_folder/example.py:1 in `<module>`
Warning
Activating trace can make your program noticeably slower if it has many points where Failure
is often created.
Warning
collect_traces
is not thread safe, beware to use it with threading!
Warning
Traces are meant to be used during development only.
Context Manager/Decorator to active traces collect to the Failures.
>>> from inspect import FrameInfo
>>> from returns.io import IOResult
>>> from returns.result import Result
>>> from returns.primitives.tracing import collect_traces
>>> with collect_traces():
... traced_failure = Result.from_failure('Traced Failure')
>>> non_traced_failure = IOResult.from_failure('Non Traced Failure')
>>> assert non_traced_failure.trace is None
>>> assert isinstance(traced_failure.trace, list)
>>> assert all(isinstance(trace_line, FrameInfo) for trace_line in traced_failure.trace)
>>> for trace_line in traced_failure.trace:
... print(f'{trace_line.filename}:{trace_line.lineno} in `{trace_line.function}`')
...
/returns/returns/result.py:525 in `Failure`
/returns/returns/result.py:322 in `from_failure`
/example_folder/example.py:1 in `<module>`
function (Optional
[TypeVar
(_FunctionType
, bound= Callable
)]) –
Union
[TypeVar
(_FunctionType
, bound= Callable
), ContextManager
[None
]]
We provide a custom mypy
plugin to fix existing issues,
provide new awesome features,
and improve type-safety of things developers commonly use.
You will need to install mypy
separately.
It is not bundled with returns
.
To install any mypy
plugin add it
to the plugins
section of the config file.
[mypy]
plugins =
returns.contrib.mypy.returns_plugin
We recommend to always add our plugin as the first one in chain.
You can have a look at the suggested mypy
configuration
in our own repository.
You can also use nitpick
tool to enforce the same mypy
configuration for all your projects.
We recommend to use our own setup. Add this to your pyproject.toml
:
[tool.nitpick]
style = "https://raw.githubusercontent.com/wemake-services/wemake-python-styleguide/master/styles/mypy.toml"
And use flake8
to lint that configuration
defined in the setup matches yours.
This will allow to keep them in sync with the upstream.
kind
feature adds Higher Kinded Types (HKT) support
curry
feature allows to write typed curried functions
partial
feature allows to write typed partial application
flow
feature allows to write better typed functional pipelines
with flow
function
pipe
feature allows to write better typed functional pipelines
with pipe
function
Used for typed partial
function.
Used for typed curry
decorator.
Used for typed flow
call.
Used for typed pipe
call.
Used for HKT emulation.
Used for Do Notation.
Custom mypy plugin to solve the temporary problem with python typing.
Important: we don’t do anything ugly here. We only solve problems of the current typing implementation.
mypy
API docs are here:
https://mypy.readthedocs.io/en/latest/extending_mypy.html
We use pytest-mypy-plugins
to test that it works correctly, see:
https://github.com/mkurnikov/pytest-mypy-plugins
Ensures that attribute access to KindN
is correct.
In other words:
from typing import TypeVar
from returns.primitives.hkt import KindN
from returns.interfaces.mappable import MappableN
_MappableType = TypeVar('_MappableType', bound=MappableN)
kind: KindN[_MappableType, int, int, int]
reveal_type(kind.map) # will work correctly!
ctx (AttributeContext
) –
Type
Infers real type behind Kind
form.
Basically, it turns Kind[IO, int]
into IO[int]
.
The only limitation is that it works with
only Instance
type in the first type argument position.
So, dekind(KindN[T, int])
will fail.
ctx (FunctionContext
) –
Type
Returns the internal function wrapped as Kinded[def]
.
Works for Kinded
class when __call__
magic method is used.
See returns.primitives.hkt.Kinded
for more information.
ctx (MethodSigContext
) –
CallableType
Reveals the correct return type of Kinded.__call__
method.
Turns -> KindN[I, t1, t2, t3]
into -> I[t1, t2, t3]
.
Also strips unused type arguments for KindN
, so:
- KindN[IO, int, <nothing>, <nothing>]
will be IO[int]
- KindN[Result, int, str, <nothing>]
will be Result[int, str]
It also processes nested KindN
with recursive strategy.
See returns.primitives.hkt.Kinded
for more information.
ctx (MethodContext
) –
Type
This hook is used to make typed curring a thing in returns project.
This plugin is a temporary solution to the problem.
It should be later replaced with the official way of doing things.
One day functions will have better API and we plan
to submit this plugin into mypy
core plugins,
so it would not be required.
Internally we just reduce the original function’s argument count. And drop some of them from function’s signature.
ctx (FunctionContext
) –
Type
Helps to analyze flow
function calls.
By default, mypy
cannot infer and check this function call:
>>> from returns.pipeline import flow
>>> assert flow(
... 1,
... lambda x: x + 1,
... lambda y: y / 2,
... ) == 1.0
But, this plugin can!
It knows all the types for all lambda
functions in the pipeline.
How?
We use the first passed parameter as the first argument to the first passed function
We use parameter + function to check the call and reveal types of current pipeline step
We iterate through all passed function and use previous return type as a new parameter to call current function
ctx (FunctionContext
) –
Type
Typing pipe
functions requires several phases.
It is pretty obvious from its usage:
When we pass a sequence of functions we have to reduce
the final callable type, it is require to match the callable
protocol.
And at this point we also kinda try
to check that all pipeline functions do match,
but this is impossible to do 100% correctly at this point,
because generic functions don’t have a type argument
to infer the final result
When we call the function, we need to check for two things. First, we check that passed argument fits our instance requirement. Second, we check that pipeline functions match. Now we have all arguments to do the real inference.
We also need to fix generic in method signature. It might be broken, because we add new generic arguments and return type. So, it is safe to reattach generic back to the function.
Here’s when it works:
>>> from returns.pipeline import pipe
>>> def first(arg: int) -> bool:
... return arg > 0
>>> def second(arg: bool) -> str:
... return 'bigger' if arg else 'not bigger'
>>> pipeline = pipe(first, second) # `analyzed` is called
>>> assert pipeline(1) == 'bigger' # `signature and `infer` are called
>>> assert pipeline(0) == 'not bigger' # `signature and `infer` again
This hook helps when we create the pipeline from sequence of funcs.
ctx (FunctionContext
) –
Type
Used to handle validation and error types in Do Notation.
What it does?
For all types we ensure that only a single container type is used in a single do-notation. We don’t allow mixing them.
For types with error types (like Result
),
it inferes what possible errors types can we have.
The result is a Union
of all possible errors.
Ensures that expression passed into .do
method is literal.
Checks that default value is provided
if generator expression has if
conditions inside.
ctx (MethodContext
) –
Type
We use special pytest
plugin to improve the testing side of this project.
For example: it is a popular request to ensure that your container does have its error pass handled. Because otherwise, developers might forget to do it properly. It is impossible to fix with types, but is really simple to check with tests.
You will need to install pytest
separately.
There’s no need to install anything special.
pytest
will automatically find and use this plugin.
To use it in your tests, request returns
fixture like so:
def test_my_container(returns):
...
We have a special helper to compare containers’ equality.
It might be an easy task for two Result
or Maybe
containers,
but it is not very easy for two ReaderResult
or FutureResult
instances.
Take a look:
>>> from returns.result import Result
>>> from returns.context import Reader
>>> assert Result.from_value(1) == Result.from_value(1)
>>> Reader.from_value(1) == Reader.from_value(1)
False
So, we can use assert_equal()
or returns.assert_equal
method from our pytest
fixture:
>>> from returns.result import Success
>>> from returns.context import Reader
>>> from returns.contrib.pytest import ReturnsAsserts
>>> def test_container_equality(returns: ReturnsAsserts):
... returns.assert_equal(Success(1), Success(1))
... returns.assert_equal(Reader.from_value(1), Reader.from_value(1))
>>> # We only run these tests manually, because it is a doc example:
>>> returns_fixture = getfixture('returns')
>>> test_container_equality(returns_fixture)
The first helper we define is is_error_handled
function.
It tests that containers do handle error track.
>>> from returns.result import Failure, Success
>>> from returns.contrib.pytest import ReturnsAsserts
>>> def test_error_handled(returns: ReturnsAsserts):
... assert not returns.is_error_handled(Failure(1))
... assert returns.is_error_handled(
... Failure(1).lash(lambda _: Success('default value')),
... )
>>> # We only run these tests manually, because it is a doc example:
>>> returns_fixture = getfixture('returns')
>>> test_error_handled(returns_fixture)
We recommend to unit test big chunks of code this way. This is helpful for big pipelines where you need at least one error handling at the very end.
This is how it works internally:
Methods like fix
and lash
mark errors
inside the container as handled
Methods like map
and alt
just copies
the error handling state from the old container to a new one,
so there’s no need to re-handle the error after these methods
Methods like bind
create new containers with unhandled errors
Note
We use monkeypathing of containers inside tests to make this check possible. They are still purely functional inside. It does not affect production code.
Sometimes we have to know if a container is created correctly in a specific point of our flow.
assert_trace
helps us to check exactly this by
identifying when a container is
created and looking for the desired function.
>>> from returns.result import Result, Success, Failure
>>> from returns.contrib.pytest import ReturnsAsserts
>>> def desired_function(arg: str) -> Result[int, str]:
... if arg.isnumeric():
... return Success(int(arg))
... return Failure('"{0}" is not a number'.format(arg))
>>> def test_if_failure_is_created_at_convert_function(
... returns: ReturnsAsserts,
... ):
... with returns.assert_trace(Failure, desired_function):
... Success('not a number').bind(desired_function)
>>> def test_if_success_is_created_at_convert_function(
... returns: ReturnsAsserts,
... ):
... with returns.assert_trace(Success, desired_function):
... Success('42').bind(desired_function)
>>> # We only run these tests manually, because it is a doc example:
>>> returns_fixture = getfixture('returns')
>>> test_if_failure_is_created_at_convert_function(returns_fixture)
>>> test_if_success_is_created_at_convert_function(returns_fixture)
We also ship a bunch of pre-defined markers with returns
:
returns_lawful
is used to mark all tests generated
by our hypothesis plugin
Bases: object
Class with helpers assertions to check containers.
errors_handled (Dict
[int
, Any
]) –
Can compare two containers even with extra calling and awaiting.
backend (str
) –
None
Ensures that container has its error handled in the end.
bool
Ensures that a given function was called during execution.
Use it to determine where the failure happened.
trace_type (TypeVar
(_ReturnsResultType
, bound= Union
[ResultLikeN, Callable
[...
, ResultLikeN]])) –
function_to_search (TypeVar
(_FunctionType
, bound= Callable
)) –
Iterator
[None
]
Hook to be executed on import.
We use it define custom markers.
None
Returns class with helpers assertions to check containers.
Iterator
[ReturnsAsserts
]
Custom assert
function to compare two any containers.
The important note here is that
this assert
should probably used in tests.
Not real application code.
It will call all Reader
based containers and await
all Future
based ones.
It also works recursively.
For example, ReaderFutureResult
will be called and then awaited.
You can specify different dependencies to call your containers.
And different backends to await
then using anyio
.
By the way, anyio
should be installed separately.
backend (str
) –
None
We provide several extra features for Hypothesis users.
And encourage to use it together with returns
.
You will need to install hypothesis
separately.
It is not bundled with returns
.
We also require anyio
package for this plugin to work with async laws.
We support a hypothesis
entrypoint
that is executed on hypothesis
import.
There we are regestering all our containers as strategies. So, you don’t have to. Example:
from returns.result import Result
from hypothesis import strategies as st
assert st.from_type(Result).example()
This is a convenience thing only.
We provide a utility function
to create hypothesis
strategy from any container.
You can use it to easily register your own containers.
from hypothesis import strategies as st
from returns.contrib.hypothesis.containers import strategy_from_container
st.register_type_strategy(
YourContainerClass,
strategy_from_container(YourContainerClass),
)
You can also pass use_init
keyword argument
if you wish to use __init__
method to instantiate your containers.
Turned off by default.
Example:
st.register_type_strategy(
YourContainerClass,
strategy_from_container(YourContainerClass, use_init=True),
)
Or you can write your own hypothesis
strategy. It is also fine.
We also provide a very powerful mechanism of checking defined container laws. It works in a combitation with “Laws as Values” feature we provide in the core.
from returns.contrib.hypothesis.laws import check_all_laws
from your.module import YourCustomContainer
check_all_laws(YourCustomContainer)
This one line of code will generate ~100 tests for all defined law
in both YourCustomContainer
and all its super types,
including our internal ones.
We also provide a way to configure
the checking process with settings_kwargs
:
check_all_laws(YourCustomContainer, settings_kwargs={'max_examples': 500})
This will increase the number of generated test to 500.
We support all kwargs from @settings
, see
@settings docs.
You can also change how hypothesis
creates instances of your container.
By default, we use .from_value
, .from_optional
, and .from_failure
if we are able to find them.
But, you can also pass types without these methods,
but with __init__
defined:
from typing import Callable, TypeVar
from typing_extensions import final
from returns.interfaces.mappable import Mappable1
from returns.primitives.container import BaseContainer
from returns.primitives.hkt import SupportsKind1
_ValueType = TypeVar('_ValueType')
_NewValueType = TypeVar('_NewValueType')
@final
class Number(
BaseContainer,
SupportsKind1['Number', _ValueType],
Mappable1[_ValueType],
):
def __init__(self, inner_value: _ValueType) -> None:
super().__init__(inner_value)
def map(
self,
function: Callable[[_ValueType], _NewValueType],
) -> 'Number[_NewValueType]':
return Number(function(self._inner_value))
# We want to allow ``__init__`` method to be used:
check_all_laws(Number, use_init=True)
As you see, we don’t support any from
methods here.
But, __init__
would be used to generate values thanks to use_init=True
.
By default, we don’t allow to use __init__
,
because there are different complex types
like Future
, ReaderFutureResult
, etc
that have complex __init__
signatures.
And we don’t want to mess with them.
Checking laws is not compatible with pytest-xdist
,
because we use a lot of global mutable state there.
Please, use returns_lawful
marker
to exclude them from pytest-xdist
execution plan.
Used to register all our types as hypothesis strategies.
See: https://hypothesis.readthedocs.io/en/latest/strategies.html
But, beware that we only register concrete types here, interfaces won’t be registered!
Our types that we register in hypothesis to be working with st.from_type
Creates a strategy from a container type.
Basically, containers should not support __init__
even when they have one.
Because, that can be very complex: for example FutureResult
requires
Awaitable[Result[a, b]]
as an __init__
value.
But, custom containers pass use_init
if they are not an instance of ApplicativeN
and do not have a working .from_value
method.
For example, pure MappableN
can do that.
We also try to resolve generic arguments.
So, Result[_ValueType, Exception]
will produce any value for success cases
and only exceptions for failure cases.
container_type (Type
[Lawful
]) –
use_init (bool
) –
Callable
[[type
], SearchStrategy
]
Special alias to define laws as functions even inside a class
Bases: Immutable
Base class for all laws. Does not have an attached signature.
Should not be used directly.
Use Law1
, Law2
or Law3
instead.
Callable
¶Function used to define this law.
Returns a name of the given law. Basically a name of the function.
Bases: Law
, Generic
[_TypeArgType1
, _ReturnType
]
Law definition for functions with a single argument.
function (Callable
[[TypeVar
(_TypeArgType1
)], TypeVar
(_ReturnType
)]) –
Bases: Law
, Generic
[_TypeArgType1
, _TypeArgType2
, _ReturnType
]
Law definition for functions with two arguments.
function (Callable
[[TypeVar
(_TypeArgType1
), TypeVar
(_TypeArgType2
)], TypeVar
(_ReturnType
)]) –
Bases: Law
, Generic
[_TypeArgType1
, _TypeArgType2
, _TypeArgType3
, _ReturnType
]
Law definition for functions with three argument.
function (Callable
[[TypeVar
(_TypeArgType1
), TypeVar
(_TypeArgType2
), TypeVar
(_TypeArgType3
)], TypeVar
(_ReturnType
)]) –
Function to check all defined mathematical laws in a specified container.
Should be used like so:
from returns.contrib.hypothesis.laws import check_all_laws
from returns.io import IO
check_all_laws(IO)
You can also pass different hypothesis
settings inside:
check_all_laws(IO, {'max_examples': 100})
Note
Cannot be used inside doctests because of the magic we use inside.
See also
container_type (Type
[Lawful
]) –
settings_kwargs (Optional
[Dict
[str
, Any
]]) –
use_init (bool
) –
None
Registers all types inside a container to resolve to a correct strategy.
For example, let’s say we have Result
type.
It is a subtype of ContainerN
, MappableN
, BindableN
, etc.
When we check this type, we need MappableN
to resolve to Result
.
Can be used independently from other functions.
container_type (Type
[Lawful
]) –
settings (_Settings
) –
Iterator
[None
]
Temporary registers a container if it is not registered yet.
container_type (Type
[Lawful
]) –
use_init (bool
) –
Iterator
[None
]
We follow Semantic Versions since the 1.0.0
release.
Versions before 1.0.0
are 0Ver
-based:
incremental in minor, bugfixes only are patches.
See 0Ver.
Fixed HKT to_str
example in documentation
Fixed backward deserialization compatibility for BaseContainer
Now requires mypy>=1.2
Fixes a problem with do-notation
and type aliases
Fixes custom pickle protocol to handle None
values gracefully
Removes broken drylabs.io link in README
Revises pointfree documentation
Adds do
notation
Adds attempt
decorator
Check __slots__\ `
correctness with``slotscheck`
New Year Release! 🎄
Now requires typing_extensions>=4.0
Now requires mypy>=0.930
Removes plugin for @safe
, @maybe
, @future
, etc.
Because we now use ParamSpec
type to properly type decorators
Fixes __slots__
not being set properly in containers and their base classes
Fixes patching of containers in pytest plugin not undone after each test
Enables Pattern Matching support for Result
containers
Enables Pattern Matching support for Maybe
container
Enables Pattern Matching support for IOResult
container
Improves hypothesis
plugin, now we detect
when type cannot be constructed and give a clear error message
Adds the option to pass what exceptions @safe
will handle
Makes _Nothing
a singleton
Refactor flow
function to be faster
Fixes that assert_trace
was not catching containers
from @safe
-wrapped functions
Fixes typos in documentation
Adds Higher Kinded Types partial support
Breaking: drops python3.6
support
Breaking: makes our mypy
plugin not optional, but required!
Breaking: changes all RequiresContext
-based type arguments order,
previously we used to specify _EnvType
as the first type argument,
now it is the last one. This is done to respect new HKT rules
Breaking: renames .rescue
to .lash
Breaking: removes all old interfaces from primitives/interfaces.py
,
use new typeclasses instead
Breaking: Maybe
is fully reworked to be lawful
Breaking: removes value_or
pointfree method,
because it is impossible to express with HKT
Breaking: removes .value_or
, .unwrap
, and .failure
methods
from FutureResult
and RequiresContext
-based types,
because we do require these methods to raise an exception on failure,
but these methods were lazy and did not raise the required exception
Breaking: changes how is_successful
is typed:
now we allow any Unwrappable
interface instances there,
including custom ones
Breaking: changes UnwrapFailedError
constructor,
now it does accept an Unwrappable
instance instead of a BaseContainer
Breaking: removes .fix
method from all containers,
also removes fix
pointfree function
Breaking: Removes coalesce
function,
because it is impossible to properly type it
Breaking: Removes all Context*
based types with .ask()
method,
use new .ask()
methods on the Reader
-based containers
Breaking: Now Future
and FutureResult
can be awaited multiple times
Breaking: Removes .unify()
method from several containers,
use unify()
pointfree function instead
Breaking: Removes .from_iterable
method from all containers,
instead adds better iterables
support,
we now have returns.iterables
module with Fold
helper
Breaking: Renames property empty
to no_args
of
all RequiresContext
-based classes
Adds new public interfaces: see returns.interfaces
Adds methods
package with several helpful things inside
Adds FutureSuccess
and FutureFailure
unit functions
to be similar to Result
and IOResult
Adds .swap
method to Result
, IOResult
, FutureResult
,
and other result based containers
Adds .modify_env
method to all RequiresContext*
types
Adds .rescue
to Maybe
Adds .equals
methods to types that can be compared directly:
Result
, Maybe
, IO
, IOResult
Adds missing from_requires_context_future_result
to RequiresContext
Adds .from_optional
and .bind_optional
to Maybe
container
Adds __slots__
to UnwrapFailedError
with halted_container
Changes flatten
to work with KindN
and any possible container
Adds a helper to test traces to our pytest
plugin
Adds cond
function to pointfree
and methods
packages
Adds compose_result
HKT method and pointfree function
Adds unify
HKT pointfree function
Adds bimap
pointfree function
Adds unwrap_or_failure
function to methods
package
Adds collect_trace
helper function for better development experience
Adds hypothesis
integration and pre-defined “monad laws as values”
Adds assert_equal
method to our pytest
plugin
Breaking: fixes serious typing issue and changes how flow
works
Breaking: fixes serious typing issue and changes how pipe
works,
now it has a hard limit of 20 parameters
Fixes that RequiresContextFutureResult
was not supported by pytest
plugin
Fixes incorrect partial
behaviour in an edge case, #618
Fixes that .apply
method of IOResult
was working incorrectly,
it was returning IOFailure(2)
as a result of IOFailure(1).apply(IOFailure(2))
Fixes bug that safe(tap(...))
was revealing invalid types sometimes
Adds a lot of new typetests
Checks that now all math laws are checked for all types
Changes docs structure, adds new Interfaces
, HKT
, and Methods
pages
Changed __str__
method in BaseContainer
class to __repr__
method
Adds Quickstart
guide
Breaking: renames mypy plugin from decorator_plugin
to returns_plugin
because of a complete rewrite and lots of new features
Breaking: changes @safe
, @impure
, impure_safe
, @maybe
semantics:
they do not work with async
functions anymore;
now you are forced to use Future
and its helpers
to work with async
functions
Breaking: renames Maybe.new
to Maybe.from_value
.
Because all our other containers support this protocol.
Only Maybe
was different, sorry for that!
Breaking: renames .from_success()
to .from_value()
,
there’s no need in two separate methods
Breaking: renames .from_successful_io()
to .from_io()
,
there’s no need in two separate methods
Breaking: renames .from_successful_context()
to .from_context()
,
there’s no need in two separate methods
Breaking: since we now support .apply()
method,
there’s no more need in *_squash
converters, they are removed
Breaking: renamed Instanceable
to Applicative
Breaking: changes .from_io
and .from_failed_io
of IOResult
to return Any
instead of NoReturn
unfilled type
Breaking: removes .lift
and .lift_*
methods from all containers,
use map_
, bind_result
, bind_io
, and other pointfree helpers instead
Breaking: removes @pipeline
function. It was a mistake:
it does not work with mixed container types,
it does not type failures properly,
it does not work with IO
and Future
,
it enforces to write imperative code in a functional codebase.
Use flow
instead
Adds typed partial
and curry
mypy plugins!
Adds typed flow
plugin, now it can accept any number of arguments,
it now also has excellent type inference
Adds typed pipe
plugin, now it can accept any number of arguments,
it now also has good type inference
Adds managed
pipeline function that is useful
for working with stateful computations
Adds typed map_
, fix
, and alt
pointfree functions
Adds typed bind_result
, bind_io
, bind_ioresult
,
bind_context
, bind_context_result
, bind_future
,
bind_async
, and bind_awaitable
pointfree functions
Adds typed bind_async_future
and bind_async_future_result
pointfree functions
Adds typed unify
pointfree function
Adds typed apply
pointfree function
Adds typed value_or
pointfree function
Adds pytest
plugin with the ability to tests error handling
Adds Future
container to easily work with async
functions
Adds FutureResult
container to easily work
with async
function that might fail
Adds RequiresContextFutureResult
container
Adds ReaderFutureResult
alias for RequiresContextFutureResult
Adds RequiresContextFutureResultE
and ReaderFutureResultE
aliases
Adds Future
, FutureResult
and RequiresContextFutureResult
support for all existing pointfree functions
Adds bind_io
method to IOResult
Adds bind_io
method to RequiresContextIOResult
Adds or_else
method to Maybe
Adds .from_io
and .from_failed_io
to RequiresContextIOResult
Syncs naming in from_*
methods, now all parameters are named inner_value
Adds not_
composition helper
Adds flatten
support for Future
,
FutureResult
and RequiresContextFutureResult
Adds __copy__
and __deepcopy__
magic methods to Immutable
class
Speeds up is_successful
function
Makes all Context
context helpers abstract,
so you cannot create new instances of this class,
also adds __slots__
to these classes
Improves RequiresContext*
types with NoDeps
where it is logically true
Fixes that @safe
decorator was generating incorrect signatures
for functions with Any
Fixes that .rescue()
of RequiresContextResult
was returning Any
Fixes that .rescue()
of RequiresContextIOResult
was returning Any
Fixes that RequiresContextResult
and RequiresContextIOResult
were not final
Fixes that ImmutableStateError
was not a subclass of AttributeError
Fixes that IOResult
was not showing str
representation
of wrapped inner_value
Replaces pytest-asyncio
with anyio
plugin,
now we test compatibility with any IO stack: asyncio
, trio
, curio
Updates lots of dependencies
Adds lots of new tests
Updates lots of docs
Removes “IO marker” name from docs in favor for “IO container”, it is not special at all. Why would we call it differently?
Breaking: renames join
to flatten
, sorry!
Breaking: renames box
to bind
and moves it to returns.pointfree
Breaking: removes Maybe.rescue
and Maybe.fix
methods
Breaking: renames io_squash
to squash_io
and moves it to returns.converters
Breaking: moves all interfaces from returns.primitives.container
to
returns.primitives.interfaces
Adds rescue
pointfree function
Adds ResultE
alias for Result[..., Exception]
Adds RequiresContext
container and Context
helper class
Adds RequiresContext
support for bind
pointfree function
Adds RequiresContext
support for flatten
function
Adds RequiresContextResult
container
Adds RequiresContextResultE
alias
Adds ReaderResult
and ReaderResultE
aliases
for RequiresContextResult[..., ..., Exception]
Adds RequiresContextResult
support for bind
and rescue
Adds RequiresContextResult
support for flatten
Adds IOResult
helper to work better with IO[Result[a, b]]
Adds IOResultE
alias for IOResult[a, Exception]
Adds IOResult
support for bind
Adds IOResult
support for flatten
Adds IOResult
support for @pipeline
Adds IOResult
support for coalesce
Adds IOResult
support for is_successful
Adds RequiresContextIOResult
container
Adds RequiresContextIOResultE
alias
Adds ReaderIOResult
and ReaderIOResultE
aliases
for RequiresContextIOResult[..., ..., Exception]
Adds RequiresContextIOResult
support for bind
and rescue
Adds RequiresContextIOResult
support for flatten
Adds Result.lift
, Maybe.lift
, RequiresContext.lift
,
and RequiresContextResult.lift
functions in addition to IO.lift
Adds Immutable
primitive type
Adds Unitable
protocol and .from_success()
and .from_failure()
methods for all Result
related classes
Adds Instanceable
protocol and .from_value()
method
for IO
and RequiresContext
Adds flow
function, which is similar to pipe
Adds swap
converter for Result
and IOResult
Adds squash_context
function to squash RequiresContext
similar to IO
Now Success
and Failure
(both io
and pure) return Any
and not NoReturn
Fixes how flatten
works, also adds more tests and docs about Failure
case
Fixes Unwrappable
type being parametrized with only one TypeVar
Changes Success
and Failure
to return Any
instead of NoReturn
Updates poetry
version in travis
Improves pipe
docs with lambda
and Generic
problem
Improves docs in several places
Now examples in docs tries to be docstests where possible
Changes how tests are checked with mypy
in CI
Breaking: now @pipeline
requires a container type when created:
@pipeline(Result)
or @pipeline(Maybe)
Maybe
and Result
now has success_type
and failure_type
aliases
Adds Result.unify
utility method for better error type composition
We now support dry-python/classes
as a first-class citizen
Adds io_squash
to squash several IO
containers into one container
with a tuple inside, currently works with 9
containers max at a time
Adds untap
function which does convert return type to None
Fixes that containers were not usable with multiprocessing
Changes the inheritance order, now BaseContainer
is the first child
Fixes that Nothing
had incorrect docstrings
Now generated
package is protected
Updates poetry
to 1.0
Breaking: now pipe()
does not require argument to be the first value,
instead it is required to use: pipe(f1, f2, f3, f4)(value)
Breaking: dropped everything from returns/__init__.py
,
because we now have quite a lot of stuff
Breaking: dropped support of zero argument functions for Nothing.fix
Breaking: dropped support of zero argument functions for Nothing.rescue
Maybe
now has .failure()
to match the same API as Result
Adds identity
function
Adds tap
function
Now pipe
allows to pipe 8 steps
Adds coalesce_result
and coalesce_maybe
converters
Fixes that code inside .fix
and .rescue
of Maybe
might be called twice
Now all methods have doctests
Updates docs about Success
and _Success
, Failure
and _Failure
Updates docs about @pipeline
Typechecks async functions and decorators inside typesafety/
tests
Breaking: python>=3.7,<=3.7.2
are not supported anymore,
because of a bug inside typing
module
Breaking: Now bind
does not change the type of an error
Breaking: Now rescue
does not change the type of a value
Breaking: Renames map_failure
to alt
Adds box()
function with the ability
to box function for direct container composition like:
a -> Container[b]
to Container[a] -> Container[b]
Adds IO.lift()
function to lift a -> a
to IO[a] -> IO[a]
Adds pipe()
function to pipeline.py
Adds __hash__()
magic methods to all containers
Changes Any
to NoReturn
in Success
and Failure
Now all type parameters in Result
, Maybe
, and IO
are covariant
Massive docs rewrite
Updates mypy
version
Updates wemake-python-styleguide
and introduces nitpick
Updates pytest-plugin-mypy
, all tests now use yml
Provides a bunch of primitive interfaces to write your own containers
Adds .map_failure()
method
Adds flatten()
function to join nested containers
Fixes type of Maybe.fix
and Maybe.rescue
to work with both lambda: 1
and lambda _: 1
Improves README
Reintroduces the Maybe
container, typed!
Introduces converters from one type to another
Adds mypy
plugin to type decorators
Complete rewrite of Result
types
Partial API change, now Success
and Failure
are not types, but functions
New internal types introduced: FixableContainer
and ValueUnwrapContainer
Fixes issue when you could return IO
container from Result.bind
Fixes @pipeline
return type
Reapplied all types to .py
files
Improved docs about IO
and Container
concept
Adds docs about container composition
Moves from Alpha
to Beta
Adds IO
container
Adds unsafe
module with unsafe functions
Changes how functions are located inside the project
Fixes container type in @pipeline
Now is_successful
is public
Now raise_exception
is public
Changes how str()
function works for container types
Total rename to “container” in the source code
safe
and pipeline
now supports asyncio
is_successful
now returns Literal
types if possible
Adds compose
helper function
Adds public API to import returns
Adds raise_exception
helper function
Adds full traceback to .unwrap()
Updates multiple dev-dependencies, including mypy
Now search in the docs is working again
Relicenses this project to BSD
Fixes copyright notice in the docs
Moves all types to .pyi
files
Renames all classes according to new naming pattern
HUGE improvement of types
Renames fmap
to map
Renames do_notation
to pipeline
, moves it to functions.py
Renames ebind
to rescue
Renames efmap
to fix
Renames container
to Container
Removes Maybe
container, since typing does not have NonNullable
type
Adds py.typed
file to be PEP561
compatible
returns
¶The project is renamed to returns
and moved to dry-python
org.
Adds .pyi
files for all modules,
to enable mypy
support for 3rd party users
Adds Maybe
container
Adds immutability and __slots__
to all containers
Adds methods to work with failures
Adds safe
decorator to convert exceptions to Result
container
Adds is_successful()
function to detect if your result is a success
Adds failure()
method to unwrap values from failed containers
Changes the type of .bind
method for Success
container
Changes how equality works, so now Failure(1) != Success(1)
Changes how new instances created on unused methods
Improves docs
Changes how PyPI
renders package’s page
Improves README
with new badges and installation steps
Initial release. Featuring only Result
and do_notation
.