Skip to main content

Type-safe Monads

Build PyPI version codecov Code style: black

This is an experiment in building monads in Python supported by strict type annotations. The goal is to be able to compose monads with the type checker ensuring their correctness.

Motivation

I'm a fan of monads, but believe they work best with the support of a strong type system. I've attempted to use libraries like PyMonad, but been frustrated by a lack of type constraints preventing incorrect usage. I could've attempted to add type annotations to one of those libraries, but building my own is more fun.

This is a fork of the original work by Correl Roush

I added some utility methods to make it easier to use in my day to day code and better interate with the pythonic style ( ie List Comprehension )

Installation

$ pip install typed-monads 

Documentation

You can find all the docs here

Curring

Mixing Higher order functions ( functions that return a function ) with moand is a very common programming style other functional programming languages. With curry decorator you can transform a function in a curried function: just apss some positional parameters and get back a function with the remaining ones.

@curry
def power(exp: int, base: int ) -> int:
    return math.pow(base, exp)

square_fn = power(2) # a function that returns the square of the parameter

Base Classes

Functor

map (*)

Applies a function to the contents of a functor, transforming it from one thing to another.

The * operator implements map on functors, and is both left and right associative:

def wordcount(s: str):
    return len(s.split())


f.map(wordcount) == wordcount * f == f * wordcount

Applicative

Extends Functor.

pure

Wraps a value in an applicative functor.

e.g.:

Maybe.pure("abc") == Just("abc")
Result.pure(123) == Ok(123)

apply (&)

Transforms the value contained in the instance's functor with a function wrapped in the same type of functor.

The & operator implements apply on applicatives, and is right-associative.

e.g.:

increment = lambda x: x + 1

Just(3).apply(Just(increment)) == Just(increment) & Just(3) == Just(4)

This can be very handily combined with map to apply curried functions to multiple arguments:

subtract = lambda x: lambda y: x - y

subtract * Just(10) & Just(4) == Just(6)

Monad

Extends Applicative.

bind (>>)

Passes the value within the monad through an operation returning the same type of monad, allowing multiple operations to be chained.

The >> operator implements bind on monads, and is left-associative.

@curry
def lookup(key: str, dictionary: Dict[str, str]) -> Maybe[str]:
    try:
        return Just(dictionary[key])
    except KeyError:
        return Nothing()


result = Just({"hello": "world"}).bind(lookup("hello")).bind(lambda s: s.upper())
result = (
    Just({"hello": "world"})
    >> lookup("hello")
    >> (lambda s: s.upper())
)

Monoid

mappend (+)

Describes an associative binary operation for a type.

mzero

Provides an identity value for the mappend operation.

mconcat

Accumulates a list of values using mappend. Returns the mzero value if the list is empty.

Monads

Wrapped values should be immutable: they are protected from accidental direct writing with Final type and the pythonic naming convention.

Maybe[T]

Represents optional data. A Maybe instance of a certain type T will either be a Just object wrapping a value of that type, or Nothing.

  • Mapping a function over Nothing will return Nothing without calling the function.
  • Binding an operation with a Nothing will return Nothing without attempting the operation.

Result[T, E]

Represents a state of success or failure, declaring a type for each. A Result instance will either be an Ok object wrapping a value of the success type T, or an Err object wrapping a value of the failure type E.

  • Mapping a function over an Err will return the Err unchanged without calling the function.
  • Binding an operation with an Err will return the Err unchanged without attempting the operation.

List[T]

Represents a ordered sequence of items.

  • Also implements Monoid.

Set[T]

Represents a unordered sequence of unique items.

  • Also implements Monoid.

Future[T]

Represents an asynchronous action.

  • Also implements Awaitable.

Reader[T]

Represents the application of a function to it's argument.

Monads as iterable

It is handy to iterate over some monad contents. List is obliviously the first candidate:

m_list: List[int] = List([1, 2, 4, 9])
for i in m_list:
    ...

#Or filter with a generator

evens: List[int] = [k for k in m_list if k % 2 == 0 ]

If you want to something to happen just if a Maybe monad is defined

for n in Just("one"):
  ...

The same apply for Results

Release files for typed-monads 0.4.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for typed-monads 0.4.8
File Size Uploaded
typed_monads-0.4.8.tar.gz 18.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for typed-monads 0.4.8
File Interpreter ABI Platform
typed_monads-0.4.8-py3-none-any.whl Python 3 none any Details

Total release size: 35.4 kB

Release files / typed_monads-0.4.8.tar.gz

Download URL typed_monads-0.4.8.tar.gz
Size 18.9 kB
Tags Source
SHA-256 checksum
How to use checksums
8cd5dba1304da81a6eab91def2fbadf260efcacc83f686d5154c82b852503079
BLAKE2b-256 checksum
How to use checksums
9580f2613cc2784b5ef475986c8589224a27968d271256b28befab1f353ad38f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.9.19

Release files / typed_monads-0.4.8-py3-none-any.whl

Download URL typed_monads-0.4.8-py3-none-any.whl
Size 16.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
140e992a5ccd0ef34e131e983c1c75b686434fd9a93ad58f47cbe39d5534d3a0
BLAKE2b-256 checksum
How to use checksums
61a809b311d70c210ea869b4bea66b3648b3856ce75f18020389eef4aad13591
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.0 CPython/3.9.19

Release history Release notifications | RSS feed

This release

0.4.8 This release

2 release files

0.4.7

2 release files

0.4.6

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page