Primitive types¶
We have several utility types that we use for our containers, that can also help end users as well.
Fold¶
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.
Immutable¶
This class is useful when you need to make some instances immutable (like our containers are immutable).
API Reference¶
Iterables¶
classDiagram AbstractFold <|-- Fold
- class AbstractFold[source]¶
Bases:
object
A collection of different helpers to write declarative
Iterable
actions.Allows to work with iterables.
Implementation
AbstractFold
andFold
types are special. They have double definition for each method: public and protected ones. Why?Because you cannot override
@kinded
method due to amypy
bug. So, there are two opportunities for us here:Declare all method as
@final
and do not allow to change anythingUse 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 overriddenProtected 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
orAbstractFold
and then change an implementation of any unkinded protected method.- final classmethod loop(iterable, acc, function)[source]¶
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/137Is also quite similar to
reduce
.Public interface for
_loop
method. Cannot be modified directly.- Parameters:
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
)]])
- Return type:
KindN
[TypeVar
(_ApplicativeKind
, bound=ApplicativeN
),TypeVar
(_UpdatedType
),TypeVar
(_SecondType
),TypeVar
(_ThirdType
)]
- final classmethod collect(iterable, acc)[source]¶
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.- Parameters:
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
)])
- Return type:
KindN
[TypeVar
(_ApplicativeKind
, bound=ApplicativeN
),tuple
[TypeVar
(_FirstType
),...
],TypeVar
(_SecondType
),TypeVar
(_ThirdType
)]
- final classmethod collect_all(iterable, acc)[source]¶
Transforms an iterable of containers into a single container.
This method only works with
FailableN
subtypes, not just anyApplicativeN
likecollect()
.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.
- class Fold[source]¶
Bases:
AbstractFold
Concrete implementation of
AbstractFold
of end users.Use it by default.
Types¶
classDiagram AttributeError <|-- ImmutableStateError
- class Immutable[source]¶
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.
Exceptions¶
classDiagram AttributeError <|-- ImmutableStateError Exception <|-- UnwrapFailedError
- exception UnwrapFailedError(container)[source]¶
Bases:
Exception
Raised when a container can not be unwrapped into a meaningful value.
- Parameters:
container (
Unwrappable
)
- halted_container¶
- exception ImmutableStateError[source]¶
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 modificationIt is used inside
typing.py
this way, we do have several typing features that requires that behaviour