Skip to content

apfel.core.monad

Monadic interfaces that enable structured ways to compose and manipulate computations on a single effectful construction.

The module defines three abstract classes: Functor, Applicative, and Monad.

Rationale

Monadic abstractions like Functor, Applicative, and Monad are popularized by Haskell. Although Python has weak support for functional programming, we include these abstractions to provide a uniform interface for such calculations.

Under the hood, these abstractions use dynamic single dispatch provided in apfel.core.dispatch, which allows us to define monadic helper functions for standard built-in types. This module provides default implementations for list, tuple, set, and function as Functor, Applicative, and Monad, dict as Functor.

Note that we loosen the constraints of such abstractions, as writing pure functional code is not a goal of this library.

Usage

Here’s a high-level comparison of the three. Assume Value is a Monad. A Monad is always a Functor and an Applicative.

value = Value(42)
assert value.map ( lambda x: x + 1 ) == Value(43)
assert value.apply(Value(lambda x: x + 1)) == Value(43)
assert value.bind (lambda x: Value(x + 1)) == Value(43)

If you are familiar with Rust, Option is a Monad, Option.map is its Functor.map, Option.and_then is its Monad.bind.

Case: list

assert Functor.map([1, 2, 3], lambda x: x + 1) == [2, 3, 4]
assert Applicative.apply([1, 2, 3], [lambda x: x + 1, lambda x: x + 2]) == [2, 3, 4, 3, 4, 5]
assert Monad.bind([1, 2, 3], lambda x: [x, x + 1]) == [1, 2, 2, 3, 3, 4]

Case: function

Functor.map of a function is exactly a composition of functions.

assert Functor.map(lambda x: x * 2, lambda x: x + 1)(2) == 5

API#

Attributes

NameDescription
fmapAn alias for Functor.map.

Classes

NameDescription
Applicativepython
Functorpython
Monadpython

fmapattributemodule attribute#

fmap = Functor.map

An alias for Functor.map.

Applicativeclass#

class Applicative(Functor, ABCDispatch)

Bases: Functor, ABCDispatch

class Applicative[T](Functor):
pure
apply

An applicative functor is a functor extended with the ability to apply an effectful function to its inner value(s).

To implement an Applicative, you need to implement at least the pure and apply methods.

See Applicative for more information.

__matmul__methodclassmethod#

def __matmul__(value: T, /) -> Self

applymethodabstract#

def apply(func: Applicative[Callable[[T], R]], /) -> Applicative[R]
def apply[F, R](self, func: F) -> Self[R]
where F: Self[Callable[[T], R]]

Applies a function wrapped inside the applicative structure to the inner value(s) of this applicative.

Parameters

NameTypeDescription
funcApplicative[Callable[[T], R]]The function to apply, wrapped within the applicative structure.

Returns

Applicative[R]
A new instance of the applicative with the transformed inner value(s).

mapmethod#

def map(func: Callable[[T], R], /) -> Applicative[R]

puremethodabstractclassmethod#

def pure(value: T, /) -> Self
def pure(cls, value: T, /) -> Self[T]

Wrap a value into the applicative structure.

Parameters

NameTypeDescription
valueTThe value to wrap.

Returns

Self
A new instance of the applicative with the value wrapped.

Functorclass#

class Functor(ABCDispatch)

Bases: ABCDispatch

class Functor[T](ABC):
map

A functor is a type that can apply a function to its inner value(s) without changing its structure.

To implement a Functor, you need to implement at least the map method.

See Functor for more information.

__xor__method#

def __xor__(func: Callable[[T], R], /) -> Functor[R]

mapmethodabstract#

def map(func: Callable[[T], R], /) -> Functor[R]
def map[F, R](self, func: F, /) -> Self[R]
where F: Callable[[T], R]

Apply a function to the inner value(s) of the functor, returning a new instance of the functor. This corresponds to the fmap in Haskell.

Built-in map function returns an iterator, not a new instance of the functor.

add = lambda x: x + 1
Functor.map([1, 2, 3], add) # [2, 3, 4]
map([1, 2, 3], add) # <map object at ...>

Parameters

NameTypeDescription
funcCallable[[T], R]The function to apply.

Returns

Functor[R]
A new instance of the functor with transformed inner value(s).

Monadclass#

class Monad(Applicative, ABCDispatch)

Bases: Applicative, ABCDispatch

class Monad[T](Applicative):
bind
(Applicative.pure)

A monad is an applicative functor extended with the ability to reuse results of previous computations and chain effectful computations together.

To implement a Monad, you need to implement at least the bind method, and the pure method from Applicative.

See Monad for more information.

applymethod#

def apply(func: Applicative[Callable[[T], R]], /) -> Monad[R]

bindmethodabstract#

def bind(func: Callable[[T], Monad[R]], /) -> Monad[R]
def bind[F, R](self, func: F) -> Self[R]
where F: Callable[[T], Self[R]]

Chain a new monadic computation to the current one.

The argument func maps a pure value of the monad’s inner type to the monadic value. bind chains this computation to the one it represents. If its computation fails, the following computations will not be executed.

The function func is called a Kleisli arrow. This bind operation has other names in different programming languages. For example, in Option monad, it is called and_then.

Parameters

NameTypeDescription
funcCallable[[T], Monad[R]]The function that returns a new monadic computation.

Returns

Monad[R]
A new instance of the monad with the result of the chained computation.

mapmethod#

def map(func: Callable[[T], R], /) -> Monad[R]

puremethodclassmethod#

def pure(value: T, /) -> Self