Source code for pymonad.maybe
# --------------------------------------------------------
# (c) Copyright 2014, 2020 by Jason DeLaat.
# Licensed under BSD 3-clause licence.
# --------------------------------------------------------
""" Implements the Maybe monad and related functions.
The Maybe monad is used to represent calculations that may or may not
return a value. Alternately, if used as function inputs, Maybe values
can be used to indicate 'optional' inputs, explicitly passing
'Nothing' when no input is required.
When creating Maybe values directly use the 'Just' function or 'Nothing':
Example:
>>> x = Just(19)
>>> y = Just('A string')
>>> z = Nothing
The 'insert' class method is a wrapper around the 'Just' function.
Example:
>>> x = Maybe.insert(9) # Same as Just(9)
"""
from typing import Any, Callable, Generic, TypeVar
import pymonad.monad
S = TypeVar("S") # pylint: disable=invalid-name
T = TypeVar("T") # pylint: disable=invalid-name
[docs]class Maybe(pymonad.monad.Monad, Generic[T]):
""" The Maybe monad class. """
[docs] @classmethod
def insert(cls, value: T) -> "Maybe[T]":
""" See Monad.insert """
return Just(value)
[docs] def amap(self: "Maybe[Callable[[S], T]]", monad_value: "Maybe[S]") -> "Maybe[T]":
""" See Monad.amap"""
if (
self.is_nothing() or monad_value.is_nothing()
): # pylint: disable=no-else-return
return Nothing
else:
return monad_value.map(self.value)
[docs] def bind(
self: "Maybe[S]", kleisli_function: "Callable[[S], Maybe[T]]"
) -> "Maybe[T]":
""" See Monad.bind """
if self.monoid is False: # pylint: disable=no-else-return
return self
else:
try:
return kleisli_function(self.value)
except: # pylint: disable=bare-except
return Nothing
[docs] def is_just(self) -> bool:
""" Returns True if the monad instance was created with the 'Just' function. """
return self.monoid
[docs] def is_nothing(self) -> bool:
""" Returns True if the monad instance is the 'Nothing' value. """
return not self.monoid
[docs] def map(self: "Maybe[S]", function: Callable[[S], T]) -> "Maybe[T]":
""" See Monad.map """
if self.is_nothing(): # pylint: disable=no-else-return
return self
else:
try:
return Just(function(self.value))
except: # pylint: disable=bare-except
return Nothing
def __eq__(self, other):
""" Checks equality of Maybe objects.
Maybe objects are equal iff:
#. They are both Nothing, or
#. They are both Just and
#. They both contain the same value.
"""
return self.value == other.value and self.monoid == other.monoid
def __repr__(self):
return f"Just {self.value}" if self.monoid else "Nothing"
[docs]def Just(value: T) -> Maybe[T]: # pylint: disable=invalid-name
""" A Maybe object representing the presence of an optional value. """
return Maybe(value, True)
# A Maybe object representing the absence of an optional value.
Nothing: Maybe[Any] = Maybe(None, False) # pylint: disable=invalid-name
[docs]class Option(
pymonad.monad.MonadAlias, Maybe[T]
): # MonadAlias must be the first parent class
""" An alias for the Maybe monad class. """
def __repr__(self):
return f"Some {self.value}" if self.monoid else "Nothing"
[docs]def Some(value: T) -> Option[T]: # pylint: disable=invalid-name
""" An Option object representing the presence of an optional value. """
return Option(value, True)