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: maximum recursion depth exceeded in comparison

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 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:

  1. Declare all method as @final and do not allow to change anything

  2. Use delegation to protected unkinded methods

We have chosen the second way! Here’s how it works:

  1. Public methods are @kinded for better typing and cannot be overridden

  2. 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.

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/137

https://i.imgur.com/Tza1isS.jpg

Is also quite similar to reduce.

Public interface for _loop method. Cannot be modified directly.

Parameters
  • iterable (Iterable[KindN[~_ApplicativeKind, ~_FirstType, ~_SecondType, ~_ThirdType]]) –

  • acc (KindN[~_ApplicativeKind, ~_UpdatedType, ~_SecondType, ~_ThirdType]) –

  • function (Callable[[~_FirstType], Callable[[~_UpdatedType], ~_UpdatedType]]) –

Return type

KindN[~_ApplicativeKind, ~_UpdatedType, ~_SecondType, ~_ThirdType]

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[~_ApplicativeKind, ~_FirstType, ~_SecondType, ~_ThirdType]]) –

  • acc (KindN[~_ApplicativeKind, Tuple[~_FirstType, …], ~_SecondType, ~_ThirdType]) –

Return type

KindN[~_ApplicativeKind, Tuple[~_FirstType, …], ~_SecondType, ~_ThirdType]

classmethod collect_all(iterable, acc)[source]

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.

Parameters
  • iterable (Iterable[KindN[~_FailableKind, ~_FirstType, ~_SecondType, ~_ThirdType]]) –

  • acc (KindN[~_FailableKind, Tuple[~_FirstType, …], ~_SecondType, ~_ThirdType]) –

Return type

KindN[~_FailableKind, Tuple[~_FirstType, …], ~_SecondType, ~_ThirdType]

class Fold[source]

Bases: returns.iterables.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 (ForwardRef) –

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:

  1. It seems kinda reasonable to expect AttributeError on attribute modification

  2. It is used inside typing.py this way, we do have several typing features that requires that behaviour

See: https://github.com/dry-python/returns/issues/394