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:
objectA collection of different helpers to write declarative
Iterableactions.Allows to work with iterables.
Implementation
AbstractFoldandFoldtypes are special. They have double definition for each method: public and protected ones. Why?Because you cannot override
@kindedmethod due to amypybug. So, there are two opportunities for us here:Declare all method as
@finaland 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
@kindedfor 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
FoldorAbstractFoldand then change an implementation of any unkinded protected method.- final classmethod loop(iterable, acc, function)[source]¶
Allows to make declarative loops for any
ApplicativeNsubtypes.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
foldlin 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
_loopmethod. 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
_collectmethod. 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
FailableNsubtypes, not just anyApplicativeNlikecollect().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_allmethod. Cannot be modified directly.
- class Fold[source]¶
Bases:
AbstractFoldConcrete implementation of
AbstractFoldof end users.Use it by default.
Types¶
classDiagram
AttributeError <|-- ImmutableStateError
- class Immutable[source]¶
Bases:
objectHelper 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.BaseContainerfor examples.
Exceptions¶
classDiagram
AttributeError <|-- ImmutableStateError
Exception <|-- UnwrapFailedError
Generic <|-- Unwrappable
- exception UnwrapFailedError(container)[source]¶
Bases:
ExceptionRaised when a container can not be unwrapped into a meaningful value.
- Parameters:
container (
Unwrappable)
- halted_container¶
- exception ImmutableStateError[source]¶
Bases:
AttributeErrorRaised when a container is forced to be mutated.
It is a sublclass of
AttributeErrorfor two reasons:It seems kinda reasonable to expect
AttributeErroron attribute modificationIt is used inside
typing.pythis way, we do have several typing features that requires that behaviour