This module provides a bunch of primitives to work with containers.
It is centered around the composition idea. Sometimes using methods on containers is not very helpful. Instead we can use functions that has the reverse semantics, but the same end result.
Why would anyone need these functions when you can use methods? To create pipelines!
Without pointfree functions you cannot easily work with containers inside pipelines. Because they do not compose well:
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!
finish_work, # does not compose either!
)
In a normal situation you would probably write:
returns_result().bind(works_with_result).bind(notifies_user)
And you need a way to somehow do this in the pipeline. That’s where pointfree functions become really useful.
Allows to compose containers and functions, but in a reverse manner.
>>> from returns.pointfree import map_
>>> from returns.maybe import Maybe, Some
>>> def mappable(arg: str) -> int:
... return ord(arg)
>>> container: Maybe[str] = Some('a')
>>> # We now have two way of composining these entities.
>>> # 1. Via ``.map``:
>>> assert container.map(mappable) == Some(97)
>>> # 2. Or via ``bind`` function, the same but in the inverse way:
>>> assert map_(mappable)(container) == Some(97)
Allows to bind a function that returns a container of the same type.
Without bind()
function
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 directly,
but how can we do it inversely?
>>> from returns.pointfree import bind
>>> from returns.maybe import Maybe, Some
>>> def bindable(arg: str) -> Maybe[int]:
... return Some(1)
>>> container: Maybe[str] = Some('a')
>>> # We now have two way of composing these entities.
>>> # 1. Via ``.bind``:
>>> assert container.bind(bindable) == Some(1)
>>> # 2. Or via ``bind`` function, the same but in the inverse way:
>>> assert bind(bindable)(container) == Some(1)
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 lash()
function is an alternative
to .lash()
container method.
It is also required for better declarative programming.
>>> from returns.pointfree import lash
>>> from returns.result import Success, Failure, Result
>>> def function(arg: str) -> Result[int, str]:
... return Success(1)
>>> container: Result[int, str] = Failure('a')
>>> # We now have two way of composing these entities.
>>> # 1. Via ``.lash``:
>>> assert container.lash(function) == Success(1)
>>> # 2. Or via ``lash`` function, the same but in the inverse way:
>>> assert lash(function)(container) == 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 function(arg: int) -> str:
... return chr(arg) + '!'
>>> assert apply(Some(function))(Some(97)) == Some('a!')
>>> assert apply(Some(function))(Some(98)) == Some('b!')
>>> assert apply(Some(function))(Nothing) == Nothing
>>> assert apply(Nothing)(Nothing) == Nothing
If you wish to use apply
inside a pipeline
that’s how it would probably look like:
>>> from returns.pointfree import apply
>>> from returns.pipeline import flow
>>> from returns.maybe import Some
>>> def function(arg: int) -> str:
... return chr(arg) + '!'
>>> assert flow(
... Some(97),
... apply(Some(function)),
... ) == Some('a!')
Or with function as the first parameter:
>>> from returns.pipeline import flow
>>> from returns.curry import curry
>>> from returns.maybe import Some
>>> @curry
... def function(first: int, second: int) -> int:
... return first + second
>>> assert flow(
... Some(function),
... 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’re able to 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
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.
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)
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)
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)
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.
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.
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
[[~_FirstType], FutureResult
[~_UpdatedType, ~_SecondType]]) –
Kinded
[Callable
[[KindN
[~_FutureResultKind, ~_FirstType, ~_SecondType, ~_ThirdType]], KindN
[~_FutureResultKind, ~_UpdatedType, ~_SecondType, ~_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
[[~_FirstType], Awaitable
[FutureResult
[~_UpdatedType, ~_SecondType]]]) –
Kinded
[Callable
[[KindN
[~_FutureResultKind, ~_FirstType, ~_SecondType, ~_ThirdType]], KindN
[~_FutureResultKind, ~_UpdatedType, ~_SecondType, ~_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.
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.
Useful alias for bind_context3()
.
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.
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.
Useful alias for modify_env3()
.
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')
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)
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.
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.
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.
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
[~_FirstType, ~_SecondType]], KindN
[~_IOResultLikeKind, ~_NewFirstType, ~_SecondType, ~_ThirdType]]) –
Kinded
[Callable
[[KindN
[~_IOResultLikeKind, ~_FirstType, ~_SecondType, ~_ThirdType]], KindN
[~_IOResultLikeKind, ~_NewFirstType, ~_SecondType, ~_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
[~_SingleFailableKind], Type
[~_DiverseFailableKind]]) –
success_value (~_ValueType) –
error_value (Optional
[~_ErrorType]) –
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.
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
[[~_FirstType], KindN
[~_DiverseFailableKind, ~_NewFirstType, ~_NewSecondType, ~_NewThirdType]]) –
Kinded
[Callable
[[KindN
[~_DiverseFailableKind, ~_FirstType, ~_SecondType, ~_ThirdType]], KindN
[~_DiverseFailableKind, ~_NewFirstType, Union
[~_SecondType, ~_NewSecondType], ~_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.