either monad

The Either monad

Data structure semantically containing either a “left” value or a “right” value, but not both.

  • Module implements a left biased either monad

    • Left values is intended for “expected” results.

    • Right value gives information on the “unexpected” perhaps “exceptional” result.

  • left and right values can be the same or different types

  • in a boolean context

    • left values are truthy

    • right values are falsy

Tip

Happy path without exceptions.

Instead of catching an exception whenever the “happy path” fails, process the left values then deal with or propagate right values.

Tip

Users of this module can avoid explicitly importing sentinel values LEFT and RIGHT by the static Either.left and Either.right class methods.

  • left_either: Either[int, str] = Either.left(42)

  • right_either: Either[int, str] = Either.right(‘Not forty-two’)

final class pythonic_fp.fptools.either.Either

Bases: Generic

either monad

Left biased Either monad.

  • immutable

  • contains either a “left” or a “right” item, but not both

  • hashable

__init__(value: L, side: T_Bool) → None
__init__(value: R, side: F_Bool) → None

init

Initialize Either instance as a left or a right Either.

param value:

The value contained in the Either.

param side:

Determines whether to produce a “left” or a “right” Either.

type side:

TF_Bool

__hash__() → int

hash

If contained value hashable, use its hash value in the hash calculation, otherwise use the value’s identity.

  • Should be safe, the Either holds a reference to the value.

  • Lazily calculates hash value, then caches it.

  • The hash also depends if the Either is a left or right.

__bool__() → bool

bool

  • left Either instances are truthy

  • right Either instances are falsy

returns:

True if Either is a left, False if a right.

__len__() → int

len

An Either always contains just one value.

returns:

1

__eq__(other: object) → bool

equality comparison

Compare Either to another object. Compare first by identity, then value.

Note

Pythonic choice was made to allow left or right Either monads to compare as equal if they have different right or left “phantom” types respectively.

  • Allows for more flexible equality checking at the expense of not flagging possible type or name mismatches.

  • Flagging such a type mismatch would require a Liskov Substitution Principle violation.

param other:

The object to be compared.

returns:

True only if other is a Either of the same side containing objects which compare as equal.

__iter__() → Iterator

iter

Yield the contained value if Either is a left.

yields:

The contained value if a left.

__repr__() → str

representation string

Return the strings

  • ‘Either(repr_value, LEFT)’ if a left

  • ‘Either(repr_value, RIGHT)’ if if a right

Where repr_value = repr(value).

returns:

A string to reproduce the Either.

__str__() → str

user string

Return the strings

  • ‘Either(str_value)’ when a left

  • ‘Either(str_value, RIGHT)’ when a right

Where str_value = str(value).

returns:

A string meaningful to an end user.

get() → L

get

Get value if a left.

returns:

The value if a left.

raises ValueError:

If not a left.

Warning

Unsafe method get will raise ValueError() if the Either is a right.

Tip

Best practice is to first check the Either in a boolean context.

get_left() → MayBe

get left

Get the value if a left.

returns:

MayBe wrapping a left value.

rtype:

MayBe[L]

get_right() → MayBe

get right

Get the value if a right.

returns:

MayBe wrapping a right value.

rtype:

MayBe[R]

map_right(f: Callable[[R], V]) → Either

map right

Map the function f over the contents of a right Either.

param f:

A function to map a right value.

returns:

A new Either instance if a right, otherwise itself.

map(f: Callable[[L], U]) → Either

map

Map function f over the Either.

param f:

Mapping function.

returns:

A new Either instance if a left, otherwise itself.

map_except(f: Callable[[L], U], fallback_right: R) → Either

map_except

Map function f over the Either with right fallback upon exception.

param f:

Mapping function.

param fallback_right:

Fallback value if exception thrown.

returns:

New left instance if successfully mapped, a propagated right, or a new right when an exception is thrown.

Note

Swallows exceptions of types

  • LookupError

  • ValueError

  • ArithmeticError

  • RuntimeError

Does not attempt to stop exceptions

  • TypeError

  • AttributeError

  • KeyboardInterrupt

bind(f: Callable[[L], Either]) → Either

bind

Flatmap function f over a left value. Propagate right values.

param f:

Function to bind.

returns:

A new Either if a left, itself if a right.

bind_except(f: Callable[[L], Either], fallback_right: R) → Either

bind_except

Flatmap function f over the Either, with fallback upon exception. Propagate right values.

param f:

Function to bind over contained values.

param fallback_right:

Fallback value if exception thrown.

returns:

A successfully bound left, a propagated right, or a right with the fallback value.

Note

Swallows exceptions of types

  • LookupError

  • ValueError

  • ArithmeticError

  • RuntimeError

Does not attempt to stop exceptions

  • TypeError

  • AttributeError

  • KeyboardInterrupt

static left(value: U) → Either

Either.left

Helper static method to explicitly construct a left Either.

param value:

The left value to use when constructing the Either.

returns:

A left Either

static right(value: V) → Either

Either.right

Helper static method to explicitly construct a left Either.

param value:

The left value to use when constructing the Either.

returns:

A right Either

static sequence(iterable_either_uv: Iterable[Either]) → Either[Iterable, V]

Either.sequence

Iterable[Either[U, V]] -> Either[Iterable[U], V]

If all Either are lefts, then return an Either of an Iterable of contained left values. Otherwise return a right Either containing the first right encountered.

param sequence_either_uv:

An Iterable of Either[U, V] values.

returns:

A left Either containing an Iterable of all the left values if none are right Either values, otherwise a right Either containing the first right value.

Note

A sequenced empty Iterable[Either[U, V]] would produce a MayBe of an empty Iterable, not an empty MayBe.

pythonic_fp.fptools.either.LEFT: Final[T_Bool] = T_Bool()

LEFT

var LEFT:

The left Either singleton flag.

pythonic_fp.fptools.either.RIGHT: Final[F_Bool] = F_Bool()

RIGHT

var RIGHT:

The right Either singleton flag