Source code for pymonad.list
# --------------------------------------------------------
# (c) Copyright 2014, 2020 by Jason DeLaat.
# Licensed under BSD 3-clause licence.
# --------------------------------------------------------
""" Implements the List monad.
The List monad is frequently used to represent calculations with
non-deterministic results, that is: functions which return more than
one (possible) result. For example, calculating how chess pieces might
move.
Example:
>>> def knight_move(position):
>>> # calculates a list of every possible square a knight could move
>>> # to from it's current position
>>> return ListMonad(position_1, position_2, ..., position_N)
>>> # A list containing every square a knight could reach after 3 moves.
>>> three_moves = (
>>> List
>>> .insert(initial_position) # However positions are defined.
>>> .then(knight_move)
>>> .then(knight_move)
>>> .then(knight_move)
>>> )
"""
from typing import (
Any,
Callable,
Generic,
List,
TypeVar,
Union,
) # pylint: disable=unused-import
import pymonad.monad
S = TypeVar("S") # pylint: disable=invalid-name
T = TypeVar("T") # pylint: disable=invalid-name
class _List(pymonad.monad.Monad, Generic[T]):
@classmethod
def insert(cls, value: T) -> "_List[T]":
return ListMonad(value)
def amap(self: "_List[Callable[[S], T]]", monad_value: "_List[S]") -> "_List[T]":
result = []
for function in self:
for value in monad_value:
result.append(function(value))
return ListMonad(*result)
def bind(
self: "_List[S]", kleisli_function: Callable[[S], "_List[T]"]
) -> "_List[T]":
return self.map(kleisli_function).join()
def join(self: "_List[_List[T]]") -> "_List[T]":
""" Flattens a nested ListMonad instance one level. """
return ListMonad(*[element for lists in self for element in lists])
def map(self: "_List[S]", function: Callable[[S], T]) -> "_List[T]":
return ListMonad(*[function(x) for x in self])
def then(
self: "_List[S]", function: Union[Callable[[S], T], Callable[[S], "_List[T]"]]
) -> "_List[T]":
try:
return self.bind(function)
except TypeError:
return self.map(function)
def __eq__(self, other):
return self.value == other.value
def __getitem__(self, index):
try:
return ListMonad(*self.value.__getitem__(index))
except TypeError:
return self.value[index]
def __iter__(self):
return iter(self.value)
def __len__(self):
return len(self.value)
def __repr__(self):
return str(self.value)
[docs]def ListMonad(*elements: List[T]) -> _List[T]: # pylint: disable=invalid-name
""" Creates an instance of the List monad.
Args:
elements: any number of elements to be inserted into the list
Returns:
An instance of the List monad.
"""
return _List(list(elements), None)
ListMonad.insert = _List.insert
ListMonad.apply = _List.apply