danom documentation

class danom.AsyncStream(seq: tuple, ops: tuple = ())

Bases: _BaseAsyncStream, Generic

A stream that applies async functions to its values.

Version changes

0.16.0: Added AsyncStream

async collect(*, workers: int = 4, use_threads: bool = False) → tuple[U, ...]

Materialise the AsyncStream into a tuple.

values = await stream.collect()
filter(fn: ~collections.abc.Callable[[~P], ~collections.abc.Awaitable[bool]], *args: ~typing.~P, **kwargs: ~typing.~P) → AsyncStream[T]

Filter the AsyncStream with an async predicate.

await stream.filter(is_even).collect()
async fold(initial: T, fn: Callable[[T, U], T], *, workers: int = 1, use_threads: bool = False) → T

Reduce the collected values into one value.

total = await stream.fold(0, add)
classmethod from_iterable(it: Iterable) → Self

Create an AsyncStream from an iterable.

from danom import AsyncStream

stream = AsyncStream.from_iterable([1, 2, 3])
map(fn: ~collections.abc.Callable[[~P], ~collections.abc.Awaitable[~danom._stream._base.U]], *args: ~typing.~P, **kwargs: ~typing.~P) → AsyncStream[T]

Map an async function to the values in the AsyncStream.

await stream.map(add_one).collect()
async partition(fn: Callable[[P], Awaitable[bool]], *, workers: int = 1, use_threads: bool = False) → tuple[AsyncStream[T], AsyncStream[U]]

Split the AsyncStream into values accepted and rejected by a predicate.

accepted, rejected = await stream.partition(is_valid)
async sequence(*, workers: int = 1, use_threads: bool = False) → Result[Self, E] | Either[Self, E]

Convert a stream of Result or Either values into one monad of stream.

result = await stream.sequence()
tap(fn: ~collections.abc.Callable[[~P], ~collections.abc.Awaitable[None]], *args: ~typing.~P, **kwargs: ~typing.~P) → AsyncStream[T]

Apply an async function to a copy of each value.

The original values remain in the AsyncStream.

await stream.tap(log_value).collect()
class danom.Either(inner: Any = None)

Bases: ABC, Generic

Either monad. Consists of Right and Left for successful and failed operations respectively. Each monad is a frozen instance to prevent further mutation.

abstractmethod and_then(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.Either[~danom._monads._either.U_co, ~danom._monads._either.E_co]], *args: ~typing.~P, **kwargs: ~typing.~P) → Either[U_co, E_co]

Pipe another function that returns a monad. For Left will return original inner.

static either_is_ok(result: Either[T_co, E_co]) → bool

Check whether the monad is ok. Allows for filter or partition in a Stream without needing a lambda or custom function.

static either_unwrap(result: Either[T_co, E_co]) → T_co

Unwrap the Right or Left monad to get the inner value.

flatten() → Either[T_co, E_co]

Flatten the monad. Will return the first Left or the lowest Right instance.

Papertrail examples:

>>> Right(inner=Right(inner=None)).flatten() == Right(inner=None)
True
>>> Right(inner=Left(inner=None)).flatten() == Left(inner=None)
True
>>> Left(inner=Right(inner=None)).flatten() == Right(inner=None)
True
>>> Left(inner=Left(inner=None)).flatten() == Left(inner=None)
True
inner: Any
abstractmethod is_ok() → bool

Returns True if the result type is Right. Returns False if the result type is Left.

Papertrail examples:

>>> Right(inner=None).is_ok() == True
True
>>> Left(inner=None).is_ok() == False
True
abstractmethod map(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.U_co], *args: ~typing.~P, **kwargs: ~typing.~P) → Either[U_co, E_co]

Pipe a pure function and wrap the return value with Right. Given an Left will return self.

Papertrail examples:

>>> Right(inner=0).map(add_one) == Right(inner=1)
True
>>> Left(inner=None).map(add_one) == Left(inner=None)
True
abstractmethod map_err(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.U_co], *args: ~typing.~P, **kwargs: ~typing.~P) → Either[U_co, E_co]

Pipe a pure function and wrap the return value with Left. Given an Right will return self.

Papertrail examples:

>>> Right(inner=0).map_err(add_one) == Right(inner=0)
True
>>> Left(inner=0).map_err(add_one) == Left(inner=1)
True
abstractmethod or_else(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.Either[~danom._monads._either.U_co, ~danom._monads._either.E_co]], *args: ~typing.~P, **kwargs: ~typing.~P) → Either[U_co, E_co]

Pipe a function that returns a monad to recover from an Left. For Right will return original Either.

classmethod unit(inner: T_co) → Right[T_co]

Unit method. Given an item of type T return Right(T)

abstractmethod unwrap() → T_co

Unwrap the Right or Left monad to get the inner value.

class danom.Err(error: E = None, input_args: tuple[()] | SafeArgs | SafeMethodArgs = (), traceback: str = '')

Bases: Result[Never, E], Generic

and_(res: Result[U, F]) → Result[U, F]

Papertrail examples:

If you call and_ on an Ok with an Err as the arg then the Err takes precedence over the Ok.

>>> Ok(inner=2).and_(Err(error="late error")) == Err(error="late error")
True

If you call and_ on an Err with an Ok as the arg then the Err still takes precedence over the Ok.

>>> Err(error="early error").and_(Ok(inner="foo")) == Err(error="early error")
True

If you call and_ on an Err with an Err as the arg then the first Err is returned and the second is discarded.

>>> Err(error="not a 2").and_(Err(error="late error")) == Err(error="not a 2")
True

Whereas, calling and_ on an Ok with another Ok as the arg then the first Ok is discarded and the second is returned.

>>> Ok(inner=2).and_(Ok(inner="different result type")) == Ok(inner="different result type")
True
and_then(fn: Callable[Concatenate[Never, P], Result[U, F]], *args: P.args, **kwargs: P.kwargs) → Result[U, F]

Papertrail examples:

An Ok passed to and_then returns the Ok produced by the function.

>>> Ok(inner=2).and_then(must_be_less_than_10) == Ok(inner=2)
True

When the function passed to and_then produces an Err, that Err becomes the result.

>>> Ok(inner=20).and_then(must_be_less_than_10) == Err(error="too high")
True

An existing Err passes through and_then unchanged, without calling the function.

>>> Err(error="not a number").and_then(must_be_less_than_10) == Err(error="not a number")
True
err() → Option[E]

Papertrail examples:

An Ok has no error, so err returns Null.

>>> Ok(inner=2).err() == Null()
True

If you call err on an Err then Some with the wrapped error is returned.

>>> Err(error="Nothing here").err() == Some(inner="Nothing here")
True
error: E
expect(msg: str) → Never

Papertrail examples:

expect extracts the value from an Ok.

>>> Ok(inner=2).expect("must be positive") == 2
True
expect_err(msg: str) → E

Papertrail examples:

expect_err extracts the error from an Err.

>>> Err(error=2).expect_err("must be err") == 2
True
flatten() → Result[Never, E]

Papertrail examples:

If you call flatten on three nested Ok values then two nested Ok values are returned. Only the outer monad is removed

>>> Ok(inner=Ok(inner=Ok(inner=2))).flatten() == Ok(inner=Ok(inner=2))
True

This is made obvious calling flatten on a doubly wrapped value returning just the inner monad.

>>> Ok(inner=Ok(inner=2)).flatten() == Ok(inner=2)
True

Calling flatten if the inner is not a monad of the same type is a no-op.

>>> Ok(inner=2).flatten() == Ok(inner=2)
True

An Err passes through flatten unchanged.

>>> Err(error=None).flatten() == Err(error=None)
True
input_args: tuple[()] | SafeArgs | SafeMethodArgs
inspect(fn: Callable[[Never], None]) → Result[Never, E]

Papertrail examples:

If you call inspect on an Ok then the original Ok is returned after the function is called, this is used for logging or side outputs that shouldn’t disrupt the existing flow.

>>> Ok(inner=[1]).inspect(append_to_list) == Ok(inner=[1])
True

If you call inspect on an Err then the Err is returned and the function is not called.

>>> Err(error="un-appendable").inspect(append_to_list) == Err(error="un-appendable")
True
inspect_err(fn: Callable[[E], None]) → Result[Never, E]

Papertrail examples:

If you call inspect_err on an Err then the original Err is returned after the function is called, it’s essentially the inverse of inspect.

>>> Err(error=[1]).inspect_err(append_to_list) == Err(error=[1])
True

Similarly to calling inspect on an Err, if you call inspect_err on an Ok then the Ok is returned and the function is not called.

>>> Ok(inner="un-appendable").inspect_err(append_to_list) == Ok(inner="un-appendable")
True
is_err() → bool

Papertrail examples:

is_err reports False for an Ok.

>>> Ok(inner=2).is_err() == False
True

For an Err, is_err reports True.

>>> Err(error=2).is_err() == True
True
is_err_and(fn: Callable[[E], bool]) → bool

Papertrail examples:

is_err_and requires both the monad to be an Err and the returned value of the passed in callable to be True, for example, if you called is_err_and with a function that checks for evenness on an Err(1) then the result is False.

>>> Err(error=1).is_err_and(is_even) == False
True

Therefore, if you call is_err_and on an Err with a function that returns True then True is returned.

>>> Err(error=2).is_err_and(is_even) == True
True

Whereas, if you call is_err_and on an Ok with a function then False is returned and the function is not called.

>>> Ok(inner=2).is_err_and(is_even) == False
True
is_ok() → bool

Papertrail examples:

is_ok reports True for an Ok.

>>> Ok(inner=2).is_ok() == True
True

For an Err, is_ok reports False.

>>> Err(error=2).is_ok() == False
True
is_ok_and(fn: Callable[[Never], bool]) → bool

Papertrail examples:

When the predicate rejects an Ok, is_ok_and reports False.

>>> Ok(inner=1).is_ok_and(is_even) == False
True

When the predicate accepts an Ok, is_ok_and reports True.

>>> Ok(inner=2).is_ok_and(is_even) == True
True

An Err makes is_ok_and report False without calling the predicate.

>>> Err(error=2).is_ok_and(is_even) == False
True
map(fn: Callable[Concatenate[Never, P], U], *args: P.args, **kwargs: P.kwargs) → Result[U, E]

Papertrail examples:

Mapping an Ok applies the function and wraps its result in a new Ok.

>>> Ok(inner=1).map(add_one) == Ok(inner=2)
True

Mapping an Err leaves it unchanged and skips the function.

>>> Err(error=1).map(add_one) == Err(error=1)
True
map_err(fn: Callable[Concatenate[E, P], F], *args: P.args, **kwargs: P.kwargs) → Result[Never, F]

Papertrail examples:

Mapping an Err applies the function and wraps its result in a new Err.

>>> Err(error=1).map_err(add_one) == Err(error=2)
True

Mapping an Ok leaves it unchanged and skips the function.

>>> Ok(inner=1).map_err(add_one) == Ok(inner=1)
True
map_or(default: U, fn: Callable[Concatenate[Never, P], U], *args: P.args, **kwargs: P.kwargs) → U

Papertrail examples:

For an Ok, map_or uses the function result instead of the default.

>>> Ok(inner="foo").map_or(42, len) == 3
True

For an Err, map_or returns the supplied default.

>>> Err(error=None).map_or(42, len) == 42
True
map_or_else(default: Callable[P, U], fn: Callable[Concatenate[Never, P], U], *args: P.args, **kwargs: P.kwargs) → U

Papertrail examples:

An Ok makes map_or_else use the mapping function.

>>> Ok(inner="foo").map_or_else(get_42, len) == 3
True

An Err makes map_or_else use the default function.

>>> Err(error=None).map_or_else(get_42, len) == 42
True
ok() → Option[Never]

Papertrail examples:

An Ok converts to Some through ok.

>>> Ok(inner=2).ok() == Some(inner=2)
True

An Err converts to Null through ok.

>>> Err(error=2).ok() == Null()
True
or_(res: Result[Never, F]) → Result[Never, F]

Papertrail examples:

An Ok keeps its value when or_ receives an Err.

>>> Ok(inner=2).or_(Err(error="foo")) == Ok(inner=2)
True

An Err gives way to an Ok passed to or_.

>>> Err(error="foo").or_(Ok(inner=100)) == Ok(inner=100)
True

When both values are Ok, or_ keeps the first one.

>>> Ok(inner=2).or_(Ok(inner=100)) == Ok(inner=2)
True

When both values are Err, or_ keeps the first error.

>>> Err(error="foo").or_(Err(error="foo")) == Err(error="foo")
True
or_else(fn: Callable[Concatenate[E, P], F], *args: P.args, **kwargs: P.kwargs) → Result[Never, F]

Papertrail examples:

An Ok passes through or_else without calling the function.

>>> Ok(inner="barbarians").or_else(get_ok_vikings) == Ok(inner="barbarians")
True

For an Err, or_else returns the Ok produced by the function.

>>> Err(error="foo").or_else(get_ok_vikings) == Ok(inner="vikings")
True

If the fallback also produces an Err, or_else keeps the original error.

>>> Err(error="foo").or_else(Err) == Err(error="foo")
True
traceback: str
transpose() → Option[Result[Never, E]]

Papertrail examples:

Transposing Ok(Some(value)) produces Some(Ok(value)).

>>> Ok(inner=Some(inner=5)).transpose() == Some(inner=Ok(inner=5))
True

Transposing Ok(Null()) produces Null.

>>> Ok(inner=Null()).transpose() == Null()
True

Transposing an Err wraps it in Some.

>>> Err(error=None).transpose() == Some(inner=Err(error=None))
True
unwrap() → Never

Papertrail examples:

unwrap extracts the value from an Ok.

>>> Ok(inner=2).unwrap() == 2
True
unwrap_err() → E

Papertrail examples:

unwrap_err extracts the error from an Err.

>>> Err(error="failed").unwrap_err() == "failed"
True
unwrap_or(default: U) → U

Papertrail examples:

With an Ok, unwrap_or returns the value and ignores the default.

>>> Ok(inner="car").unwrap_or("bike") == "car"
True

With an Err, unwrap_or returns the default.

>>> Err(error=None).unwrap_or("bike") == "bike"
True
unwrap_or_else(fn: Callable[[E], U]) → U

Papertrail examples:

An Ok makes unwrap_or_else return its value without calling the function.

>>> Ok(inner=4).unwrap_or_else(get_42) == 4
True

An Err makes unwrap_or_else return the function result.

>>> Err(error=None).unwrap_or_else(get_42) == 42
True
class danom.Left(inner: Any = None)

Bases: Either[Never, E_co]

and_then(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.Either[~danom._monads._either.U_co, ~danom._monads._either.E_co]], *args: ~typing.~P, **kwargs: ~typing.~P) → Self

Pipe another function that returns a monad. For Left will return original inner.

is_ok() → Literal[False]

Papertrail examples:

>>> Right(inner=None).is_ok() == True
True
>>> Left(inner=None).is_ok() == False
True
map(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.U_co], *args: ~typing.~P, **kwargs: ~typing.~P) → Self

Papertrail examples:

>>> Right(inner=0).map(add_one) == Right(inner=1)
True
>>> Left(inner=None).map(add_one) == Left(inner=None)
True
map_err(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.U_co], *args: ~typing.~P, **kwargs: ~typing.~P) → Left[F_co]

Papertrail examples:

>>> Right(inner=0).map_err(add_one) == Right(inner=0)
True
>>> Left(inner=0).map_err(add_one) == Left(inner=1)
True
or_else(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.Either[~danom._monads._either.U_co, ~danom._monads._either.E_co]], *args: ~typing.~P, **kwargs: ~typing.~P) → Either[U_co, E_co]

Pipe a function that returns a monad to recover from an Left. For Right will return original Either.

unwrap() → T_co

Unwrap the Right or Left monad to get the inner value.

class danom.Null

Bases: Option, Generic

and_(opt_b: Option[T]) → Option[T]

Papertrail examples:

A Some combined with Null through and_ produces Null.

>>> Some(inner=2).and_(Null()) == Null()
True

A Null combined with an Some through and_ remains Null.

>>> Null().and_(Some(inner="foo")) == Null()
True

When both options contain values, and_ keeps the second Some.

>>> Some(inner=2).and_(Some(inner="foo")) == Some(inner="foo")
True

Combining two Null values with and_ produces Null.

>>> Null().and_(Null()) == Null()
True
and_then(fn: Callable[[T], Option[U]]) → Option[U]

Papertrail examples:

A successful function passed to and_then returns its Some result.

>>> Some(inner=2).and_then(must_be_less_than_10) == Some(inner=2)
True

When the function returns Null, and_then passes that Null through.

>>> Some(inner=20).and_then(must_be_less_than_10) == Null()
True

An existing Null skips the function passed to and_then.

>>> Null().and_then(must_be_less_than_10) == Null()
True
as_list() → list[T]

Papertrail examples:

A Some becomes a one-item list through as_list.

>>> Some(inner=2).as_list() == [2]
True

as_list represents Null as an empty list.

>>> Null().as_list() == []
True
as_tuple() → tuple[T, ...]

Papertrail examples:

A Some becomes a one-item tuple through as_tuple.

>>> Some(inner=2).as_tuple() == (2,)
True

as_tuple represents Null as an empty tuple.

>>> Null().as_tuple() == ()
True
expect(msg: str) → T

Papertrail examples:

expect extracts the value from a Some.

>>> Some(inner=2).expect("must be positive") == 2
True
filter_(predicate: Callable[[T], bool]) → Option[T]

Papertrail examples:

A Some that fails the predicate becomes Null through filter_.

>>> Some(inner=3).filter_(is_even) == Null()
True

A Some that passes the predicate remains unchanged.

>>> Some(inner=4).filter_(is_even) == Some(inner=4)
True

filter_ leaves Null unchanged without calling the predicate.

>>> Null().filter_(is_even) == Null()
True
flatten() → Option[T]

Papertrail examples:

Flattening three nested Some values removes only the outer option.

>>> Some(inner=Some(inner=Some(inner=2))).flatten() == Some(inner=Some(inner=2))
True

Flattening a doubly wrapped value returns the inner Some.

>>> Some(inner=Some(inner=2)).flatten() == Some(inner=2)
True

Flattening a Some with a non-option value does nothing.

>>> Some(inner=2).flatten() == Some(inner=2)
True

A Null passes through flatten unchanged.

>>> Null().flatten() == Null()
True
inspect(fn: Callable[[T], None]) → Option[T]

Papertrail examples:

Inspecting a Some calls the function and returns the original option.

>>> Some(inner=[1]).inspect(append_to_list) == Some(inner=[1])
True

Inspecting Null returns Null without calling the function.

>>> Null().inspect(append_to_list) == Null()
True
is_none() → bool

Papertrail examples:

is_none reports False for a Some.

>>> Some(inner=2).is_none() == False
True

For Null, is_none reports True.

>>> Null().is_none() == True
True
is_none_or(fn: Callable[[T], bool]) → bool

Papertrail examples:

When the predicate rejects a Some, is_none_or reports False.

>>> Some(inner=1).is_none_or(is_even) == False
True

When the predicate accepts a Some, is_none_or reports True.

>>> Some(inner=2).is_none_or(is_even) == True
True

Null makes is_none_or report True without calling the predicate.

>>> Null().is_none_or(is_even) == True
True
is_some() → bool

Papertrail examples:

is_some reports True for a Some.

>>> Some(inner=2).is_some() == True
True

For Null, is_some reports False.

>>> Null().is_some() == False
True
is_some_and(fn: Callable[[T], bool]) → bool

Papertrail examples:

When the predicate rejects a Some, is_some_and reports False.

>>> Some(inner=1).is_some_and(is_even) == False
True

When the predicate accepts a Some, is_some_and reports True.

>>> Some(inner=2).is_some_and(is_even) == True
True

A Null makes is_some_and report False without calling the predicate.

>>> Null().is_some_and(is_even) == False
True
map(fn: Callable[[T], U]) → Option[U]

Papertrail examples:

Mapping a Some applies the function and wraps its result in a new Some.

>>> Some(inner=1).map(add_one) == Some(inner=2)
True

Mapping Null leaves it unchanged and skips the function.

>>> Null().map(add_one) == Null()
True
map_or(default: U, fn: Callable[[T], U]) → U

Papertrail examples:

For a Some, map_or uses the function result instead of the default.

>>> Some(inner="foo").map_or(42, len) == 3
True

For Null, map_or returns the supplied default.

>>> Null().map_or(42, len) == 42
True
map_or_else(default: Callable[..., U], fn: Callable[[T], U]) → U

Papertrail examples:

A Some makes map_or_else use the mapping function.

>>> Some(inner="foo").map_or_else(get_42, len) == 3
True

A Null makes map_or_else use the default function.

>>> Null().map_or_else(get_42, len) == 42
True
ok_or(err: E) → Result[T, E]

Papertrail examples:

A Some converts to Ok through ok_or.

>>> Some(inner="foo").ok_or(0) == Ok(inner="foo")
True

A Null converts to Err through ok_or.

>>> Null().ok_or(0) == Err(error=0)
True
ok_or_else(err: Callable[[], E]) → Result[T, E]

Papertrail examples:

A Some converts to Ok without calling the error function.

>>> Some(inner="foo").ok_or_else(get_42) == Ok(inner="foo")
True

A Null converts to Err using the error function result.

>>> Null().ok_or_else(get_42) == Err(error=42)
True
or_(opt_b: Option[T]) → Option[T]

Papertrail examples:

A Some keeps its value when or_ receives Null.

>>> Some(inner=2).or_(Null()) == Some(inner=2)
True

A Null gives way to a Some passed to or_.

>>> Null().or_(Some(inner=100)) == Some(inner=100)
True

When both options contain values, or_ keeps the first Some.

>>> Some(inner=2).or_(Some(inner=100)) == Some(inner=2)
True

When both options are Null, or_ returns Null.

>>> Null().or_(Null()) == Null()
True
or_else(opt_b: Callable[[], Option[T]]) → Option[T]

Papertrail examples:

A Some passes through or_else without calling the function.

>>> Some(inner="barbarians").or_else(get_some_vikings) == Some(inner="barbarians")
True

For Null, or_else returns the Some produced by the function.

>>> Null().or_else(get_some_vikings) == Some(inner="vikings")
True

If the fallback also produces Null, or_else returns Null.

>>> Null().or_else(Null) == Null()
True
replace(value: T) → Option[T]

Papertrail examples:

Replacing a Some returns a new Some with the replacement value.

>>> Some(inner=2).replace(5) == Some(inner=5)
True

Replacing Null leaves it as Null.

>>> Null().replace(3) == Null()
True
transpose() → Result[Option[T], E]

Papertrail examples:

Transposing Some(Ok(value)) produces Ok(Some(value)).

>>> Some(inner=Ok(inner=2)).transpose() == Ok(inner=Some(inner=2))
True

Transposing Some(Err(error)) produces Err(Some(error)).

>>> Some(inner=Err(error=2)).transpose() == Err(error=Some(inner=2))
True

Transposing Null produces Ok(Null()).

>>> Null().transpose() == Ok(inner=Null())
True
unwrap() → T

Papertrail examples:

unwrap extracts the value from a Some.

>>> Some(inner=2).unwrap() == 2
True
unwrap_or(default: T) → T

Papertrail examples:

With a Some, unwrap_or returns the value and ignores the default.

>>> Some(inner="car").unwrap_or("bike") == "car"
True

With Null, unwrap_or returns the default.

>>> Null().unwrap_or("bike") == "bike"
True
unwrap_or_else(fn: Callable[[], T]) → T

Papertrail examples:

A Some makes unwrap_or_else return its value without calling the function.

>>> Some(inner=4).unwrap_or_else(get_42) == 4
True

A Null makes unwrap_or_else return the function result.

>>> Null().unwrap_or_else(get_42) == 42
True
unzip() → tuple[Option[T], Option[U]]

Papertrail examples:

Unzipping a Some pair produces two Some values.

>>> Some(inner=(2, 2)).unzip() == (Some(inner=2), Some(inner=2))
True

Unzipping a Some with a non-pair value produces two Null values.

>>> Some(inner=4).unzip() == (Null(), Null())
True

Unzipping Null produces two Null values.

>>> Null().unzip() == (Null(), Null())
True
zip(other: Option[U]) → Option[tuple[T, U]]

Papertrail examples:

Zipping two Some values produces a Some containing both values.

>>> Some(inner=1).zip(Some(inner="hi")) == Some(inner=(1, "hi"))
True

Zipping a Some with Null produces Null.

>>> Some(inner=1).zip(Null()) == Null()
True

Zipping Null with a Some produces Null.

>>> Null().zip(Some(inner=1)) == Null()
True
class danom.Ok(inner: T = None)

Bases: Result[T, Never], Generic

and_(res: Result[U, E]) → Result[U, E]

Papertrail examples:

If you call and_ on an Ok with an Err as the arg then the Err takes precedence over the Ok.

>>> Ok(inner=2).and_(Err(error="late error")) == Err(error="late error")
True

If you call and_ on an Err with an Ok as the arg then the Err still takes precedence over the Ok.

>>> Err(error="early error").and_(Ok(inner="foo")) == Err(error="early error")
True

If you call and_ on an Err with an Err as the arg then the first Err is returned and the second is discarded.

>>> Err(error="not a 2").and_(Err(error="late error")) == Err(error="not a 2")
True

Whereas, calling and_ on an Ok with another Ok as the arg then the first Ok is discarded and the second is returned.

>>> Ok(inner=2).and_(Ok(inner="different result type")) == Ok(inner="different result type")
True
and_then(fn: Callable[Concatenate[T, P], Result[U, E]], *args: P.args, **kwargs: P.kwargs) → Result[U, E]

Papertrail examples:

An Ok passed to and_then returns the Ok produced by the function.

>>> Ok(inner=2).and_then(must_be_less_than_10) == Ok(inner=2)
True

When the function passed to and_then produces an Err, that Err becomes the result.

>>> Ok(inner=20).and_then(must_be_less_than_10) == Err(error="too high")
True

An existing Err passes through and_then unchanged, without calling the function.

>>> Err(error="not a number").and_then(must_be_less_than_10) == Err(error="not a number")
True
err() → Option[E]

Papertrail examples:

An Ok has no error, so err returns Null.

>>> Ok(inner=2).err() == Null()
True

If you call err on an Err then Some with the wrapped error is returned.

>>> Err(error="Nothing here").err() == Some(inner="Nothing here")
True
expect(msg: str) → T

Papertrail examples:

expect extracts the value from an Ok.

>>> Ok(inner=2).expect("must be positive") == 2
True
expect_err(msg: str) → Never

Papertrail examples:

expect_err extracts the error from an Err.

>>> Err(error=2).expect_err("must be err") == 2
True
flatten() → Result[T, Never]

Papertrail examples:

If you call flatten on three nested Ok values then two nested Ok values are returned. Only the outer monad is removed

>>> Ok(inner=Ok(inner=Ok(inner=2))).flatten() == Ok(inner=Ok(inner=2))
True

This is made obvious calling flatten on a doubly wrapped value returning just the inner monad.

>>> Ok(inner=Ok(inner=2)).flatten() == Ok(inner=2)
True

Calling flatten if the inner is not a monad of the same type is a no-op.

>>> Ok(inner=2).flatten() == Ok(inner=2)
True

An Err passes through flatten unchanged.

>>> Err(error=None).flatten() == Err(error=None)
True
inner: T
inspect(fn: Callable[[T], None]) → Result[T, Never]

Papertrail examples:

If you call inspect on an Ok then the original Ok is returned after the function is called, this is used for logging or side outputs that shouldn’t disrupt the existing flow.

>>> Ok(inner=[1]).inspect(append_to_list) == Ok(inner=[1])
True

If you call inspect on an Err then the Err is returned and the function is not called.

>>> Err(error="un-appendable").inspect(append_to_list) == Err(error="un-appendable")
True
inspect_err(fn: Callable[[Never], None]) → Result[T, Never]

Papertrail examples:

If you call inspect_err on an Err then the original Err is returned after the function is called, it’s essentially the inverse of inspect.

>>> Err(error=[1]).inspect_err(append_to_list) == Err(error=[1])
True

Similarly to calling inspect on an Err, if you call inspect_err on an Ok then the Ok is returned and the function is not called.

>>> Ok(inner="un-appendable").inspect_err(append_to_list) == Ok(inner="un-appendable")
True
is_err() → bool

Papertrail examples:

is_err reports False for an Ok.

>>> Ok(inner=2).is_err() == False
True

For an Err, is_err reports True.

>>> Err(error=2).is_err() == True
True
is_err_and(fn: Callable[[Never], bool]) → bool

Papertrail examples:

is_err_and requires both the monad to be an Err and the returned value of the passed in callable to be True, for example, if you called is_err_and with a function that checks for evenness on an Err(1) then the result is False.

>>> Err(error=1).is_err_and(is_even) == False
True

Therefore, if you call is_err_and on an Err with a function that returns True then True is returned.

>>> Err(error=2).is_err_and(is_even) == True
True

Whereas, if you call is_err_and on an Ok with a function then False is returned and the function is not called.

>>> Ok(inner=2).is_err_and(is_even) == False
True
is_ok() → bool

Papertrail examples:

is_ok reports True for an Ok.

>>> Ok(inner=2).is_ok() == True
True

For an Err, is_ok reports False.

>>> Err(error=2).is_ok() == False
True
is_ok_and(fn: Callable[[T], bool]) → bool

Papertrail examples:

When the predicate rejects an Ok, is_ok_and reports False.

>>> Ok(inner=1).is_ok_and(is_even) == False
True

When the predicate accepts an Ok, is_ok_and reports True.

>>> Ok(inner=2).is_ok_and(is_even) == True
True

An Err makes is_ok_and report False without calling the predicate.

>>> Err(error=2).is_ok_and(is_even) == False
True
map(fn: Callable[Concatenate[T, P], U], *args: P.args, **kwargs: P.kwargs) → Result[U, Never]

Papertrail examples:

Mapping an Ok applies the function and wraps its result in a new Ok.

>>> Ok(inner=1).map(add_one) == Ok(inner=2)
True

Mapping an Err leaves it unchanged and skips the function.

>>> Err(error=1).map(add_one) == Err(error=1)
True
map_err(fn: Callable[Concatenate[Never, P], F], *args: P.args, **kwargs: P.kwargs) → Result[T, F]

Papertrail examples:

Mapping an Err applies the function and wraps its result in a new Err.

>>> Err(error=1).map_err(add_one) == Err(error=2)
True

Mapping an Ok leaves it unchanged and skips the function.

>>> Ok(inner=1).map_err(add_one) == Ok(inner=1)
True
map_or(default: U, fn: Callable[Concatenate[T, P], U], *args: P.args, **kwargs: P.kwargs) → U

Papertrail examples:

For an Ok, map_or uses the function result instead of the default.

>>> Ok(inner="foo").map_or(42, len) == 3
True

For an Err, map_or returns the supplied default.

>>> Err(error=None).map_or(42, len) == 42
True
map_or_else(default: Callable[P, U], fn: Callable[Concatenate[T, P], U], *args: P.args, **kwargs: P.kwargs) → U

Papertrail examples:

An Ok makes map_or_else use the mapping function.

>>> Ok(inner="foo").map_or_else(get_42, len) == 3
True

An Err makes map_or_else use the default function.

>>> Err(error=None).map_or_else(get_42, len) == 42
True
ok() → Option[T]

Papertrail examples:

An Ok converts to Some through ok.

>>> Ok(inner=2).ok() == Some(inner=2)
True

An Err converts to Null through ok.

>>> Err(error=2).ok() == Null()
True
or_(res: Result[T, F]) → Result[T, F]

Papertrail examples:

An Ok keeps its value when or_ receives an Err.

>>> Ok(inner=2).or_(Err(error="foo")) == Ok(inner=2)
True

An Err gives way to an Ok passed to or_.

>>> Err(error="foo").or_(Ok(inner=100)) == Ok(inner=100)
True

When both values are Ok, or_ keeps the first one.

>>> Ok(inner=2).or_(Ok(inner=100)) == Ok(inner=2)
True

When both values are Err, or_ keeps the first error.

>>> Err(error="foo").or_(Err(error="foo")) == Err(error="foo")
True
or_else(fn: Callable[Concatenate[Never, P], F], *args: P.args, **kwargs: P.kwargs) → Result[T, F]

Papertrail examples:

An Ok passes through or_else without calling the function.

>>> Ok(inner="barbarians").or_else(get_ok_vikings) == Ok(inner="barbarians")
True

For an Err, or_else returns the Ok produced by the function.

>>> Err(error="foo").or_else(get_ok_vikings) == Ok(inner="vikings")
True

If the fallback also produces an Err, or_else keeps the original error.

>>> Err(error="foo").or_else(Err) == Err(error="foo")
True
transpose() → Option[Result[T, Never]]

Papertrail examples:

Transposing Ok(Some(value)) produces Some(Ok(value)).

>>> Ok(inner=Some(inner=5)).transpose() == Some(inner=Ok(inner=5))
True

Transposing Ok(Null()) produces Null.

>>> Ok(inner=Null()).transpose() == Null()
True

Transposing an Err wraps it in Some.

>>> Err(error=None).transpose() == Some(inner=Err(error=None))
True
unwrap() → T

Papertrail examples:

unwrap extracts the value from an Ok.

>>> Ok(inner=2).unwrap() == 2
True
unwrap_err() → Never

Papertrail examples:

unwrap_err extracts the error from an Err.

>>> Err(error="failed").unwrap_err() == "failed"
True
unwrap_or(default: T) → T

Papertrail examples:

With an Ok, unwrap_or returns the value and ignores the default.

>>> Ok(inner="car").unwrap_or("bike") == "car"
True

With an Err, unwrap_or returns the default.

>>> Err(error=None).unwrap_or("bike") == "bike"
True
unwrap_or_else(fn: Callable[[Never], T]) → T

Papertrail examples:

An Ok makes unwrap_or_else return its value without calling the function.

>>> Ok(inner=4).unwrap_or_else(get_42) == 4
True

An Err makes unwrap_or_else return the function result.

>>> Err(error=None).unwrap_or_else(get_42) == 42
True
class danom.Option

Bases: ABC, Generic

A monad to represent optional values.

Version changes

0.17.0: Added Option, Some, Null

abstractmethod and_(opt_b: Option[T]) → Option[T]

Papertrail examples:

A Some combined with Null through and_ produces Null.

>>> Some(inner=2).and_(Null()) == Null()
True

A Null combined with an Some through and_ remains Null.

>>> Null().and_(Some(inner="foo")) == Null()
True

When both options contain values, and_ keeps the second Some.

>>> Some(inner=2).and_(Some(inner="foo")) == Some(inner="foo")
True

Combining two Null values with and_ produces Null.

>>> Null().and_(Null()) == Null()
True
abstractmethod and_then(fn: Callable[[T], Option[U]]) → Option[T]

Papertrail examples:

A successful function passed to and_then returns its Some result.

>>> Some(inner=2).and_then(must_be_less_than_10) == Some(inner=2)
True

When the function returns Null, and_then passes that Null through.

>>> Some(inner=20).and_then(must_be_less_than_10) == Null()
True

An existing Null skips the function passed to and_then.

>>> Null().and_then(must_be_less_than_10) == Null()
True
abstractmethod as_list() → list[T]

Papertrail examples:

A Some becomes a one-item list through as_list.

>>> Some(inner=2).as_list() == [2]
True

as_list represents Null as an empty list.

>>> Null().as_list() == []
True
abstractmethod as_tuple() → tuple[T, ...]

Papertrail examples:

A Some becomes a one-item tuple through as_tuple.

>>> Some(inner=2).as_tuple() == (2,)
True

as_tuple represents Null as an empty tuple.

>>> Null().as_tuple() == ()
True
cloned() → Option[T]

Papertrail examples:

Cloning a Some creates a separate option with the same value.

>>> Some(inner=2).cloned() == Some(inner=2)
True

Cloning Null creates a separate Null.

>>> Null().cloned() == Null()
True
abstractmethod expect(msg: str) → T

Papertrail examples:

expect extracts the value from a Some.

>>> Some(inner=2).expect("must be positive") == 2
True
abstractmethod filter_(predicate: Callable[[T], bool]) → Option[T]

Papertrail examples:

A Some that fails the predicate becomes Null through filter_.

>>> Some(inner=3).filter_(is_even) == Null()
True

A Some that passes the predicate remains unchanged.

>>> Some(inner=4).filter_(is_even) == Some(inner=4)
True

filter_ leaves Null unchanged without calling the predicate.

>>> Null().filter_(is_even) == Null()
True
abstractmethod flatten() → Option[T]

Papertrail examples:

Flattening three nested Some values removes only the outer option.

>>> Some(inner=Some(inner=Some(inner=2))).flatten() == Some(inner=Some(inner=2))
True

Flattening a doubly wrapped value returns the inner Some.

>>> Some(inner=Some(inner=2)).flatten() == Some(inner=2)
True

Flattening a Some with a non-option value does nothing.

>>> Some(inner=2).flatten() == Some(inner=2)
True

A Null passes through flatten unchanged.

>>> Null().flatten() == Null()
True
abstractmethod inspect(fn: Callable[[T], None]) → Option[T]

Papertrail examples:

Inspecting a Some calls the function and returns the original option.

>>> Some(inner=[1]).inspect(append_to_list) == Some(inner=[1])
True

Inspecting Null returns Null without calling the function.

>>> Null().inspect(append_to_list) == Null()
True
abstractmethod is_none() → bool

Papertrail examples:

is_none reports False for a Some.

>>> Some(inner=2).is_none() == False
True

For Null, is_none reports True.

>>> Null().is_none() == True
True
abstractmethod is_none_or(fn: Callable[[T], bool]) → bool

Papertrail examples:

When the predicate rejects a Some, is_none_or reports False.

>>> Some(inner=1).is_none_or(is_even) == False
True

When the predicate accepts a Some, is_none_or reports True.

>>> Some(inner=2).is_none_or(is_even) == True
True

Null makes is_none_or report True without calling the predicate.

>>> Null().is_none_or(is_even) == True
True
abstractmethod is_some() → bool

Papertrail examples:

is_some reports True for a Some.

>>> Some(inner=2).is_some() == True
True

For Null, is_some reports False.

>>> Null().is_some() == False
True
abstractmethod is_some_and(fn: Callable[[T], bool]) → bool

Papertrail examples:

When the predicate rejects a Some, is_some_and reports False.

>>> Some(inner=1).is_some_and(is_even) == False
True

When the predicate accepts a Some, is_some_and reports True.

>>> Some(inner=2).is_some_and(is_even) == True
True

A Null makes is_some_and report False without calling the predicate.

>>> Null().is_some_and(is_even) == False
True
abstractmethod map(fn: Callable[[T], U]) → Option[U]

Papertrail examples:

Mapping a Some applies the function and wraps its result in a new Some.

>>> Some(inner=1).map(add_one) == Some(inner=2)
True

Mapping Null leaves it unchanged and skips the function.

>>> Null().map(add_one) == Null()
True
abstractmethod map_or(default: U, fn: Callable[[T], U]) → U

Papertrail examples:

For a Some, map_or uses the function result instead of the default.

>>> Some(inner="foo").map_or(42, len) == 3
True

For Null, map_or returns the supplied default.

>>> Null().map_or(42, len) == 42
True
abstractmethod map_or_else(default: Callable[..., U], fn: Callable[[T], U]) → U

Papertrail examples:

A Some makes map_or_else use the mapping function.

>>> Some(inner="foo").map_or_else(get_42, len) == 3
True

A Null makes map_or_else use the default function.

>>> Null().map_or_else(get_42, len) == 42
True
abstractmethod ok_or(err: E) → Result[T, E]

Papertrail examples:

A Some converts to Ok through ok_or.

>>> Some(inner="foo").ok_or(0) == Ok(inner="foo")
True

A Null converts to Err through ok_or.

>>> Null().ok_or(0) == Err(error=0)
True
abstractmethod ok_or_else(err: Callable[..., E]) → Result[T, E]

Papertrail examples:

A Some converts to Ok without calling the error function.

>>> Some(inner="foo").ok_or_else(get_42) == Ok(inner="foo")
True

A Null converts to Err using the error function result.

>>> Null().ok_or_else(get_42) == Err(error=42)
True
abstractmethod or_(opt_b: Option[T]) → Option[T]

Papertrail examples:

A Some keeps its value when or_ receives Null.

>>> Some(inner=2).or_(Null()) == Some(inner=2)
True

A Null gives way to a Some passed to or_.

>>> Null().or_(Some(inner=100)) == Some(inner=100)
True

When both options contain values, or_ keeps the first Some.

>>> Some(inner=2).or_(Some(inner=100)) == Some(inner=2)
True

When both options are Null, or_ returns Null.

>>> Null().or_(Null()) == Null()
True
abstractmethod or_else(opt_b: Callable[..., Option[T]]) → Option[T]

Papertrail examples:

A Some passes through or_else without calling the function.

>>> Some(inner="barbarians").or_else(get_some_vikings) == Some(inner="barbarians")
True

For Null, or_else returns the Some produced by the function.

>>> Null().or_else(get_some_vikings) == Some(inner="vikings")
True

If the fallback also produces Null, or_else returns Null.

>>> Null().or_else(Null) == Null()
True
abstractmethod replace(value: T) → Option[T]

Papertrail examples:

Replacing a Some returns a new Some with the replacement value.

>>> Some(inner=2).replace(5) == Some(inner=5)
True

Replacing Null leaves it as Null.

>>> Null().replace(3) == Null()
True
abstractmethod transpose() → Result[Option[T], E]

Papertrail examples:

Transposing Some(Ok(value)) produces Ok(Some(value)).

>>> Some(inner=Ok(inner=2)).transpose() == Ok(inner=Some(inner=2))
True

Transposing Some(Err(error)) produces Err(Some(error)).

>>> Some(inner=Err(error=2)).transpose() == Err(error=Some(inner=2))
True

Transposing Null produces Ok(Null()).

>>> Null().transpose() == Ok(inner=Null())
True
abstractmethod unwrap() → T

Papertrail examples:

unwrap extracts the value from a Some.

>>> Some(inner=2).unwrap() == 2
True
abstractmethod unwrap_or(default: T) → T

Papertrail examples:

With a Some, unwrap_or returns the value and ignores the default.

>>> Some(inner="car").unwrap_or("bike") == "car"
True

With Null, unwrap_or returns the default.

>>> Null().unwrap_or("bike") == "bike"
True
abstractmethod unwrap_or_else(fn: Callable[..., T]) → T

Papertrail examples:

A Some makes unwrap_or_else return its value without calling the function.

>>> Some(inner=4).unwrap_or_else(get_42) == 4
True

A Null makes unwrap_or_else return the function result.

>>> Null().unwrap_or_else(get_42) == 42
True
abstractmethod unzip() → tuple[Option[T], Option[U]]

Papertrail examples:

Unzipping a Some pair produces two Some values.

>>> Some(inner=(2, 2)).unzip() == (Some(inner=2), Some(inner=2))
True

Unzipping a Some with a non-pair value produces two Null values.

>>> Some(inner=4).unzip() == (Null(), Null())
True

Unzipping Null produces two Null values.

>>> Null().unzip() == (Null(), Null())
True
abstractmethod zip(other: Option[U]) → Option[tuple[T, U]]

Papertrail examples:

Zipping two Some values produces a Some containing both values.

>>> Some(inner=1).zip(Some(inner="hi")) == Some(inner=(1, "hi"))
True

Zipping a Some with Null produces Null.

>>> Some(inner=1).zip(Null()) == Null()
True

Zipping Null with a Some produces Null.

>>> Null().zip(Some(inner=1)) == Null()
True
class danom.ParStream(seq: tuple, ops: tuple = ())

Bases: _BaseSyncStream, Generic

A stream that applies its operations with a thread or process pool.

Version changes

0.16.0: Added ParStream

collect(*, workers: int = 4, use_threads: bool = False) → tuple[U, ...]

Materialise the ParStream with the configured workers.

workers=-1 uses one worker for each available CPU, except one. Set use_threads to True to use threads instead of processes.

from danom import ParStream

ParStream.from_iterable([1, 2, 3]).map(add_one).collect(workers=2)
to_par() → Self

Return the ParStream unchanged.

to_stream() → Stream[T]

Convert the ParStream to a synchronous Stream.

class danom.Result

Bases: ABC, Generic

A monad to represent failable values.

Version changes

0.17.0: Updated Result, Ok, Err. Added majority of methods that are used in the Result enum in Rust. There is a breaking change with how flatten works.

abstractmethod and_(res: Result[U, F]) → Result[U, F]

Papertrail examples:

If you call and_ on an Ok with an Err as the arg then the Err takes precedence over the Ok.

>>> Ok(inner=2).and_(Err(error="late error")) == Err(error="late error")
True

If you call and_ on an Err with an Ok as the arg then the Err still takes precedence over the Ok.

>>> Err(error="early error").and_(Ok(inner="foo")) == Err(error="early error")
True

If you call and_ on an Err with an Err as the arg then the first Err is returned and the second is discarded.

>>> Err(error="not a 2").and_(Err(error="late error")) == Err(error="not a 2")
True

Whereas, calling and_ on an Ok with another Ok as the arg then the first Ok is discarded and the second is returned.

>>> Ok(inner=2).and_(Ok(inner="different result type")) == Ok(inner="different result type")
True
abstractmethod and_then(fn: Callable[Concatenate[T, P], Result[U, F]], *args: P.args, **kwargs: P.kwargs) → Result[U, F]

Papertrail examples:

An Ok passed to and_then returns the Ok produced by the function.

>>> Ok(inner=2).and_then(must_be_less_than_10) == Ok(inner=2)
True

When the function passed to and_then produces an Err, that Err becomes the result.

>>> Ok(inner=20).and_then(must_be_less_than_10) == Err(error="too high")
True

An existing Err passes through and_then unchanged, without calling the function.

>>> Err(error="not a number").and_then(must_be_less_than_10) == Err(error="not a number")
True
cloned() → Result[T, E]

Papertrail examples:

Cloning an Ok creates a separate Ok with the same value.

>>> Ok(inner=2).cloned() == Ok(inner=2)
True

Cloning an Err creates a separate Err with the same value.

>>> Err(error=2).cloned() == Err(error=2)
True
abstractmethod err() → Option[E]

Papertrail examples:

An Ok has no error, so err returns Null.

>>> Ok(inner=2).err() == Null()
True

If you call err on an Err then Some with the wrapped error is returned.

>>> Err(error="Nothing here").err() == Some(inner="Nothing here")
True
abstractmethod expect(msg: str) → T

Papertrail examples:

expect extracts the value from an Ok.

>>> Ok(inner=2).expect("must be positive") == 2
True
abstractmethod expect_err(msg: str) → E

Papertrail examples:

expect_err extracts the error from an Err.

>>> Err(error=2).expect_err("must be err") == 2
True
abstractmethod flatten() → Result[T, E]

Papertrail examples:

If you call flatten on three nested Ok values then two nested Ok values are returned. Only the outer monad is removed

>>> Ok(inner=Ok(inner=Ok(inner=2))).flatten() == Ok(inner=Ok(inner=2))
True

This is made obvious calling flatten on a doubly wrapped value returning just the inner monad.

>>> Ok(inner=Ok(inner=2)).flatten() == Ok(inner=2)
True

Calling flatten if the inner is not a monad of the same type is a no-op.

>>> Ok(inner=2).flatten() == Ok(inner=2)
True

An Err passes through flatten unchanged.

>>> Err(error=None).flatten() == Err(error=None)
True
abstractmethod inspect(fn: Callable[[T], None]) → Result[T, E]

Papertrail examples:

If you call inspect on an Ok then the original Ok is returned after the function is called, this is used for logging or side outputs that shouldn’t disrupt the existing flow.

>>> Ok(inner=[1]).inspect(append_to_list) == Ok(inner=[1])
True

If you call inspect on an Err then the Err is returned and the function is not called.

>>> Err(error="un-appendable").inspect(append_to_list) == Err(error="un-appendable")
True
abstractmethod inspect_err(fn: Callable[[E], None]) → Result[T, E]

Papertrail examples:

If you call inspect_err on an Err then the original Err is returned after the function is called, it’s essentially the inverse of inspect.

>>> Err(error=[1]).inspect_err(append_to_list) == Err(error=[1])
True

Similarly to calling inspect on an Err, if you call inspect_err on an Ok then the Ok is returned and the function is not called.

>>> Ok(inner="un-appendable").inspect_err(append_to_list) == Ok(inner="un-appendable")
True
abstractmethod is_err() → bool

Papertrail examples:

is_err reports False for an Ok.

>>> Ok(inner=2).is_err() == False
True

For an Err, is_err reports True.

>>> Err(error=2).is_err() == True
True
abstractmethod is_err_and(fn: Callable[[E], bool]) → bool

Papertrail examples:

is_err_and requires both the monad to be an Err and the returned value of the passed in callable to be True, for example, if you called is_err_and with a function that checks for evenness on an Err(1) then the result is False.

>>> Err(error=1).is_err_and(is_even) == False
True

Therefore, if you call is_err_and on an Err with a function that returns True then True is returned.

>>> Err(error=2).is_err_and(is_even) == True
True

Whereas, if you call is_err_and on an Ok with a function then False is returned and the function is not called.

>>> Ok(inner=2).is_err_and(is_even) == False
True
abstractmethod is_ok() → bool

Papertrail examples:

is_ok reports True for an Ok.

>>> Ok(inner=2).is_ok() == True
True

For an Err, is_ok reports False.

>>> Err(error=2).is_ok() == False
True
abstractmethod is_ok_and(fn: Callable[[T], bool]) → bool

Papertrail examples:

When the predicate rejects an Ok, is_ok_and reports False.

>>> Ok(inner=1).is_ok_and(is_even) == False
True

When the predicate accepts an Ok, is_ok_and reports True.

>>> Ok(inner=2).is_ok_and(is_even) == True
True

An Err makes is_ok_and report False without calling the predicate.

>>> Err(error=2).is_ok_and(is_even) == False
True
abstractmethod map(fn: Callable[Concatenate[T, P], U], *args: P.args, **kwargs: P.kwargs) → Result[U, E]

Papertrail examples:

Mapping an Ok applies the function and wraps its result in a new Ok.

>>> Ok(inner=1).map(add_one) == Ok(inner=2)
True

Mapping an Err leaves it unchanged and skips the function.

>>> Err(error=1).map(add_one) == Err(error=1)
True
abstractmethod map_err(fn: Callable[Concatenate[E, P], F], *args: P.args, **kwargs: P.kwargs) → Result[T, F]

Papertrail examples:

Mapping an Err applies the function and wraps its result in a new Err.

>>> Err(error=1).map_err(add_one) == Err(error=2)
True

Mapping an Ok leaves it unchanged and skips the function.

>>> Ok(inner=1).map_err(add_one) == Ok(inner=1)
True
abstractmethod map_or(default: U, fn: Callable[Concatenate[T, P], U], *args: P.args, **kwargs: P.kwargs) → U

Papertrail examples:

For an Ok, map_or uses the function result instead of the default.

>>> Ok(inner="foo").map_or(42, len) == 3
True

For an Err, map_or returns the supplied default.

>>> Err(error=None).map_or(42, len) == 42
True
abstractmethod map_or_else(default: Callable[P, U], fn: Callable[Concatenate[T, P], U], *args: P.args, **kwargs: P.kwargs) → U

Papertrail examples:

An Ok makes map_or_else use the mapping function.

>>> Ok(inner="foo").map_or_else(get_42, len) == 3
True

An Err makes map_or_else use the default function.

>>> Err(error=None).map_or_else(get_42, len) == 42
True
abstractmethod ok() → Option[T]

Papertrail examples:

An Ok converts to Some through ok.

>>> Ok(inner=2).ok() == Some(inner=2)
True

An Err converts to Null through ok.

>>> Err(error=2).ok() == Null()
True
abstractmethod or_(res: Result[T, F]) → Result[T, F]

Papertrail examples:

An Ok keeps its value when or_ receives an Err.

>>> Ok(inner=2).or_(Err(error="foo")) == Ok(inner=2)
True

An Err gives way to an Ok passed to or_.

>>> Err(error="foo").or_(Ok(inner=100)) == Ok(inner=100)
True

When both values are Ok, or_ keeps the first one.

>>> Ok(inner=2).or_(Ok(inner=100)) == Ok(inner=2)
True

When both values are Err, or_ keeps the first error.

>>> Err(error="foo").or_(Err(error="foo")) == Err(error="foo")
True
abstractmethod or_else(fn: Callable[Concatenate[E, P], F], *args: P.args, **kwargs: P.kwargs) → Result[T, F]

Papertrail examples:

An Ok passes through or_else without calling the function.

>>> Ok(inner="barbarians").or_else(get_ok_vikings) == Ok(inner="barbarians")
True

For an Err, or_else returns the Ok produced by the function.

>>> Err(error="foo").or_else(get_ok_vikings) == Ok(inner="vikings")
True

If the fallback also produces an Err, or_else keeps the original error.

>>> Err(error="foo").or_else(Err) == Err(error="foo")
True
static result_is_ok(result: Result[T, E]) → bool

Check whether the monad is ok. Allows for filter or partition in a Stream without needing a lambda or custom function.

from danom import Stream, Result

Stream.from_iterable([Ok(), Ok(), Err()]).filter(Result.result_is_ok).collect() == (Ok(), Ok())
static result_unwrap(result: Result[T, E]) → T

Unwrap the Ok monad and get the inner value. Unwrap the Err monad will raise the inner error.

from danom import Err, Ok, Stream, Result

oks, errs = Stream.from_iterable([Ok(1), Ok(2), Err()]).partition(Result.result_is_ok)
oks.map(Result.result_unwrap).collect == (1, 2)
abstractmethod transpose() → Option[Result[T, E]]

Papertrail examples:

Transposing Ok(Some(value)) produces Some(Ok(value)).

>>> Ok(inner=Some(inner=5)).transpose() == Some(inner=Ok(inner=5))
True

Transposing Ok(Null()) produces Null.

>>> Ok(inner=Null()).transpose() == Null()
True

Transposing an Err wraps it in Some.

>>> Err(error=None).transpose() == Some(inner=Err(error=None))
True
classmethod unit(inner: T) → Result[T, E]

Unit method. Given an item of type T return Ok(T)

>>> from danom import Err, Ok, Result

>>> Result.unit(0) == Ok(0)
True

>>> Ok.unit(0) == Ok(0)
True

>>> Err.unit(0) == Ok(0)
True
abstractmethod unwrap() → T

Papertrail examples:

unwrap extracts the value from an Ok.

>>> Ok(inner=2).unwrap() == 2
True
abstractmethod unwrap_err() → E

Papertrail examples:

unwrap_err extracts the error from an Err.

>>> Err(error="failed").unwrap_err() == "failed"
True
abstractmethod unwrap_or(default: T) → T

Papertrail examples:

With an Ok, unwrap_or returns the value and ignores the default.

>>> Ok(inner="car").unwrap_or("bike") == "car"
True

With an Err, unwrap_or returns the default.

>>> Err(error=None).unwrap_or("bike") == "bike"
True
abstractmethod unwrap_or_else(fn: Callable[[E], T]) → T

Papertrail examples:

An Ok makes unwrap_or_else return its value without calling the function.

>>> Ok(inner=4).unwrap_or_else(get_42) == 4
True

An Err makes unwrap_or_else return the function result.

>>> Err(error=None).unwrap_or_else(get_42) == 42
True
class danom.Right(inner: Any = None)

Bases: Either[T_co, Never]

and_then(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.Either[~danom._monads._either.U_co, ~danom._monads._either.E_co]], *args: ~typing.~P, **kwargs: ~typing.~P) → Either[U_co, E_co]

Pipe another function that returns a monad. For Left will return original inner.

is_ok() → Literal[True]

Papertrail examples:

>>> Right(inner=None).is_ok() == True
True
>>> Left(inner=None).is_ok() == False
True
map(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.U_co], *args: ~typing.~P, **kwargs: ~typing.~P) → Right[U_co]

Papertrail examples:

>>> Right(inner=0).map(add_one) == Right(inner=1)
True
>>> Left(inner=None).map(add_one) == Left(inner=None)
True
map_err(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.U_co], *args: ~typing.~P, **kwargs: ~typing.~P) → Self

Papertrail examples:

>>> Right(inner=0).map_err(add_one) == Right(inner=0)
True
>>> Left(inner=0).map_err(add_one) == Left(inner=1)
True
or_else(func: ~collections.abc.Callable[[~typing.Concatenate[~danom._monads._either.T_co, ~P]], ~danom._monads._either.Either[~danom._monads._either.U_co, ~danom._monads._either.E_co]], *args: ~typing.~P, **kwargs: ~typing.~P) → Self

Pipe a function that returns a monad to recover from an Left. For Right will return original Either.

unwrap() → T_co

Unwrap the Right or Left monad to get the inner value.

class danom.Some(inner: T)

Bases: Option, Generic

and_(opt_b: Option[T]) → Option[T]

Papertrail examples:

A Some combined with Null through and_ produces Null.

>>> Some(inner=2).and_(Null()) == Null()
True

A Null combined with an Some through and_ remains Null.

>>> Null().and_(Some(inner="foo")) == Null()
True

When both options contain values, and_ keeps the second Some.

>>> Some(inner=2).and_(Some(inner="foo")) == Some(inner="foo")
True

Combining two Null values with and_ produces Null.

>>> Null().and_(Null()) == Null()
True
and_then(fn: Callable[[T], Option[U]]) → Option[U]

Papertrail examples:

A successful function passed to and_then returns its Some result.

>>> Some(inner=2).and_then(must_be_less_than_10) == Some(inner=2)
True

When the function returns Null, and_then passes that Null through.

>>> Some(inner=20).and_then(must_be_less_than_10) == Null()
True

An existing Null skips the function passed to and_then.

>>> Null().and_then(must_be_less_than_10) == Null()
True
as_list() → list[T]

Papertrail examples:

A Some becomes a one-item list through as_list.

>>> Some(inner=2).as_list() == [2]
True

as_list represents Null as an empty list.

>>> Null().as_list() == []
True
as_tuple() → tuple[T, ...]

Papertrail examples:

A Some becomes a one-item tuple through as_tuple.

>>> Some(inner=2).as_tuple() == (2,)
True

as_tuple represents Null as an empty tuple.

>>> Null().as_tuple() == ()
True
expect(msg: str) → T

Papertrail examples:

expect extracts the value from a Some.

>>> Some(inner=2).expect("must be positive") == 2
True
filter_(predicate: Callable[[T], bool]) → Option[T]

Papertrail examples:

A Some that fails the predicate becomes Null through filter_.

>>> Some(inner=3).filter_(is_even) == Null()
True

A Some that passes the predicate remains unchanged.

>>> Some(inner=4).filter_(is_even) == Some(inner=4)
True

filter_ leaves Null unchanged without calling the predicate.

>>> Null().filter_(is_even) == Null()
True
flatten() → Option[T]

Papertrail examples:

Flattening three nested Some values removes only the outer option.

>>> Some(inner=Some(inner=Some(inner=2))).flatten() == Some(inner=Some(inner=2))
True

Flattening a doubly wrapped value returns the inner Some.

>>> Some(inner=Some(inner=2)).flatten() == Some(inner=2)
True

Flattening a Some with a non-option value does nothing.

>>> Some(inner=2).flatten() == Some(inner=2)
True

A Null passes through flatten unchanged.

>>> Null().flatten() == Null()
True
inner: T
inspect(fn: Callable[[T], None]) → Option[T]

Papertrail examples:

Inspecting a Some calls the function and returns the original option.

>>> Some(inner=[1]).inspect(append_to_list) == Some(inner=[1])
True

Inspecting Null returns Null without calling the function.

>>> Null().inspect(append_to_list) == Null()
True
is_none() → bool

Papertrail examples:

is_none reports False for a Some.

>>> Some(inner=2).is_none() == False
True

For Null, is_none reports True.

>>> Null().is_none() == True
True
is_none_or(fn: Callable[[T], bool]) → bool

Papertrail examples:

When the predicate rejects a Some, is_none_or reports False.

>>> Some(inner=1).is_none_or(is_even) == False
True

When the predicate accepts a Some, is_none_or reports True.

>>> Some(inner=2).is_none_or(is_even) == True
True

Null makes is_none_or report True without calling the predicate.

>>> Null().is_none_or(is_even) == True
True
is_some() → bool

Papertrail examples:

is_some reports True for a Some.

>>> Some(inner=2).is_some() == True
True

For Null, is_some reports False.

>>> Null().is_some() == False
True
is_some_and(fn: Callable[[T], bool]) → bool

Papertrail examples:

When the predicate rejects a Some, is_some_and reports False.

>>> Some(inner=1).is_some_and(is_even) == False
True

When the predicate accepts a Some, is_some_and reports True.

>>> Some(inner=2).is_some_and(is_even) == True
True

A Null makes is_some_and report False without calling the predicate.

>>> Null().is_some_and(is_even) == False
True
map(fn: Callable[[T], U]) → Option[U]

Papertrail examples:

Mapping a Some applies the function and wraps its result in a new Some.

>>> Some(inner=1).map(add_one) == Some(inner=2)
True

Mapping Null leaves it unchanged and skips the function.

>>> Null().map(add_one) == Null()
True
map_or(default: U, fn: Callable[[T], U]) → U

Papertrail examples:

For a Some, map_or uses the function result instead of the default.

>>> Some(inner="foo").map_or(42, len) == 3
True

For Null, map_or returns the supplied default.

>>> Null().map_or(42, len) == 42
True
map_or_else(default: Callable[[], U], fn: Callable[[T], U]) → U

Papertrail examples:

A Some makes map_or_else use the mapping function.

>>> Some(inner="foo").map_or_else(get_42, len) == 3
True

A Null makes map_or_else use the default function.

>>> Null().map_or_else(get_42, len) == 42
True
ok_or(err: E) → Result[T, E]

Papertrail examples:

A Some converts to Ok through ok_or.

>>> Some(inner="foo").ok_or(0) == Ok(inner="foo")
True

A Null converts to Err through ok_or.

>>> Null().ok_or(0) == Err(error=0)
True
ok_or_else(err: Callable[[], E]) → Result[T, E]

Papertrail examples:

A Some converts to Ok without calling the error function.

>>> Some(inner="foo").ok_or_else(get_42) == Ok(inner="foo")
True

A Null converts to Err using the error function result.

>>> Null().ok_or_else(get_42) == Err(error=42)
True
or_(opt_b: Option[T]) → Option[T]

Papertrail examples:

A Some keeps its value when or_ receives Null.

>>> Some(inner=2).or_(Null()) == Some(inner=2)
True

A Null gives way to a Some passed to or_.

>>> Null().or_(Some(inner=100)) == Some(inner=100)
True

When both options contain values, or_ keeps the first Some.

>>> Some(inner=2).or_(Some(inner=100)) == Some(inner=2)
True

When both options are Null, or_ returns Null.

>>> Null().or_(Null()) == Null()
True
or_else(opt_b: Callable[[], Option[T]]) → Option[T]

Papertrail examples:

A Some passes through or_else without calling the function.

>>> Some(inner="barbarians").or_else(get_some_vikings) == Some(inner="barbarians")
True

For Null, or_else returns the Some produced by the function.

>>> Null().or_else(get_some_vikings) == Some(inner="vikings")
True

If the fallback also produces Null, or_else returns Null.

>>> Null().or_else(Null) == Null()
True
replace(value: T) → Option[T]

Papertrail examples:

Replacing a Some returns a new Some with the replacement value.

>>> Some(inner=2).replace(5) == Some(inner=5)
True

Replacing Null leaves it as Null.

>>> Null().replace(3) == Null()
True
transpose() → Result[Option[T], Option[T]]

Papertrail examples:

Transposing Some(Ok(value)) produces Ok(Some(value)).

>>> Some(inner=Ok(inner=2)).transpose() == Ok(inner=Some(inner=2))
True

Transposing Some(Err(error)) produces Err(Some(error)).

>>> Some(inner=Err(error=2)).transpose() == Err(error=Some(inner=2))
True

Transposing Null produces Ok(Null()).

>>> Null().transpose() == Ok(inner=Null())
True
unwrap() → T

Papertrail examples:

unwrap extracts the value from a Some.

>>> Some(inner=2).unwrap() == 2
True
unwrap_or(default: T) → T

Papertrail examples:

With a Some, unwrap_or returns the value and ignores the default.

>>> Some(inner="car").unwrap_or("bike") == "car"
True

With Null, unwrap_or returns the default.

>>> Null().unwrap_or("bike") == "bike"
True
unwrap_or_else(fn: Callable[[], T]) → T

Papertrail examples:

A Some makes unwrap_or_else return its value without calling the function.

>>> Some(inner=4).unwrap_or_else(get_42) == 4
True

A Null makes unwrap_or_else return the function result.

>>> Null().unwrap_or_else(get_42) == 42
True
unzip() → tuple[Option[T], Option[U]]

Papertrail examples:

Unzipping a Some pair produces two Some values.

>>> Some(inner=(2, 2)).unzip() == (Some(inner=2), Some(inner=2))
True

Unzipping a Some with a non-pair value produces two Null values.

>>> Some(inner=4).unzip() == (Null(), Null())
True

Unzipping Null produces two Null values.

>>> Null().unzip() == (Null(), Null())
True
zip(other: Option[U]) → Option[tuple[T, U]]

Papertrail examples:

Zipping two Some values produces a Some containing both values.

>>> Some(inner=1).zip(Some(inner="hi")) == Some(inner=(1, "hi"))
True

Zipping a Some with Null produces Null.

>>> Some(inner=1).zip(Null()) == Null()
True

Zipping Null with a Some produces Null.

>>> Null().zip(Some(inner=1)) == Null()
True
class danom.Stream(seq: tuple, ops: tuple = ())

Bases: _BaseSyncStream, Generic

A lazy iterator with functional operations.

Why bother?

Readability counts, abstracting common operations helps reduce cognitive complexity when reading code.

Comparison

Take this imperative pipeline of operations, it iterates once over the data, skipping the value if it fails one of the filter checks:

res = []

for x in range(1_000_000):
    item = triple(x)

    if not is_gt_ten(item):
        continue

    item = min_two(item)

    if not is_even_num(item):
        continue

    item = square(item)

    if not is_lt_400(item):
        continue

    res.append(item)
[100, 256]

number of tokens: 90

number of keywords: 11

keyword breakdown: {‘for’: 1, ‘in’: 1, ‘if’: 3, ‘not’: 3, ‘continue’: 3}

After a bit of experience with python you might use list comprehensions, however this is arguably _less_ clear and iterates multiple times over the same data

mul_three = [triple(x) for x in range(1_000_000)]
gt_ten = [x for x in mul_three if is_gt_ten(x)]
sub_two = [min_two(x) for x in gt_ten]
is_even = [x for x in sub_two if is_even_num(x)]
squared = [square(x) for x in is_even]
lt_400 = [x for x in squared if is_lt_400(x)]
[100, 256]

number of tokens: 92

number of keywords: 15

keyword breakdown: {‘for’: 6, ‘in’: 6, ‘if’: 3}

This still has a lot of tokens that the developer has to read to understand the code. The extra keywords add noise that cloud the actual transformations.

Using a Stream results in this:

from danom import Stream

(
    Stream.from_iterable(range(1_000_000))
    .map(triple)
    .filter(is_gt_ten)
    .map(min_two)
    .filter(is_even_num)
    .map(square)
    .filter(is_lt_400)
    .collect()
)
(100, 256)

number of tokens: 60

number of keywords: 0

keyword breakdown: {}

The business logic is arguably much clearer like this.

Version changes

0.13.0: Stream.map, Stream.filter and Stream.tap now take kwargs and partial them into the passed in function.

collect(*, workers: int = 4, use_threads: bool = False) → tuple[U, ...]

Materialise the sequence from the Stream.

from danom import Stream

stream = Stream.from_iterable([0, 1, 2, 3]).map(add_one)
stream.collect() == (1, 2, 3, 4)
to_par() → ParStream[T]

Convert the Stream to a ParStream.

to_stream() → Stream[T]

Return the Stream unchanged.

danom.all_of(*fns: Callable[[T_co], bool]) → Callable[[T_co], bool]

True if all of the given functions return True.

from danom import all_of

is_valid_user = all_of(is_subscribed, is_active, has_2fa)
is_valid_user(user) == True
danom.any_of(*fns: Callable[[T_co], bool]) → Callable[[T_co], bool]

True if any of the given functions return True.

from danom import any_of

is_eligible = any_of(has_coupon, is_vip, is_staff)
is_eligible(user) == True
danom.compose(*fns: Callable[[T_co], T_co | U_co]) → Callable[[T_co], T_co | U_co]

Compose multiple functions into one.

The functions will be called in sequence with the result of one being used as the input for the next.

from danom import compose

add_two = compose(add_one, add_one)
add_two(0) == 2
add_two_is_even = compose(add_one, add_one, is_even)
add_two_is_even(0) == True
danom.identity(x: T_co) → T_co

Basic identity function.

from danom import identity

identity("abc") == "abc"
identity(1) == 1
identity(ComplexDataType(a=1, b=2, c=3)) == ComplexDataType(a=1, b=2, c=3)

Papertrail examples:

>>> identity(1) == 1
True
>>> identity("abc") == "abc"
True
>>> identity([0, 1, 2]) == [0, 1, 2]
True
>>> identity(Ok(inner=1)) == Ok(inner=1)
True
danom.invert(func: Callable[[T_co], bool]) → Callable[[T_co], bool]

Invert a boolean function so it returns False where it would’ve returned True.

from danom import invert

invert(has_len)("abc") == False
invert(has_len)("") == True
danom.new_type(name: str, base_type: type, validators: Callable | Sequence[Callable] | None = None, converters: Callable | Sequence[Callable] | None = None, *, frozen: bool = True)

Create a NewType based on another type.

>>> from danom import new_type

>>> def is_positive[T](value: T) -> bool:
...     return value >= 0

>>> ValidBalance = new_type("ValidBalance", float, validators=[is_positive])
>>> ValidBalance(20.0) == ValidBalance(inner=20.0)
True

Unlike an inherited class, the type will not return True for an isinstance check.

isinstance(ValidBalance(20.0), ValidBalance) == True
isinstance(ValidBalance(20.0), float) == False

The methods of the given base_type will be forwarded to the specialised type. Alternatively the map method can be used to return a new type instance with the transformation.

from danom import new_type

def has_len(email: str) -> bool:
    return len(email) > 0

Email = new_type("Email", str, validators=[has_len])
Email("some_email@domain.com").upper() == "SOME_EMAIL@DOMAIN.COM"
Email("some_email@domain.com").map(str.upper) == Email(inner='SOME_EMAIL@DOMAIN.COM')
danom.none_of(*fns: Callable[[T_co], bool]) → Callable[[T_co], bool]

True if none of the given functions return True.

from danom import none_of

is_valid = none_of(is_empty, exceeds_size_limit, contains_unsupported_format)
is_valid(submission) == True
danom.safe(func: Callable[[P], U]) → Callable[[P], Result[U, Exception]]
danom.safe(func: None = None, *, errors: ExceptionType = Exception) → Callable[[Callable[[P], U]], Callable[[P], Result[U, Exception]]]

Decorator for functions that wraps the function in a try except returns Ok on success else Err.

from danom import safe

@safe
def add_one(a: int) -> int:
    return a + 1

add_one(1) == Ok(inner=2)

Only catch a single error type or subset of error types by passing in an error type to catch.

from danom import safe

@safe(errors=ZeroDivisionError)
def div(a: int, b: int) -> float:
    return a / b


div(2, 0) == Err(error=ZeroDivisionError('division by zero'))
div(2, "")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
danom.safe_method(func: Callable[[Concatenate[T, P]], U]) → Callable[[Concatenate[T, P]], Result[U, Exception]]

The same as safe except it forwards on the self of the class instance to the wrapped function.

from danom import safe_method

class Adder:
    def __init__(self, result: int = 0) -> None:
        self.result = result

    @safe_method
    def add_one(self, a: int) -> int:
        return self.result + 1

Adder.add_one(1) == Ok(inner=1)

Indices and tables