Source code for returns.maybe

from abc import ABCMeta
from functools import wraps
from typing import (
    TYPE_CHECKING,
    Any,
    Callable,
    ClassVar,
    Generator,
    Iterator,
    NoReturn,
    Optional,
    TypeVar,
    Union,
    final,
)

from typing_extensions import ParamSpec

from returns.interfaces.specific.maybe import MaybeBased2
from returns.primitives.container import BaseContainer, container_equality
from returns.primitives.exceptions import UnwrapFailedError
from returns.primitives.hkt import Kind1, SupportsKind1

# Definitions:
_ValueType = TypeVar('_ValueType', covariant=True)
_NewValueType = TypeVar('_NewValueType')

_FuncParams = ParamSpec('_FuncParams')


[docs]class Maybe( # type: ignore[type-var] BaseContainer, SupportsKind1['Maybe', _ValueType], MaybeBased2[_ValueType, None], metaclass=ABCMeta, ): """ Represents a result of a series of computations that can return ``None``. An alternative to using exceptions or constant ``is None`` checks. ``Maybe`` is an abstract type and should not be instantiated directly. Instead use ``Some`` and ``Nothing``. See also: - https://github.com/gcanti/fp-ts/blob/master/docs/modules/Option.ts.md """ __slots__ = () _inner_value: Optional[_ValueType] __match_args__ = ('_inner_value',) #: Alias for `Nothing` empty: ClassVar['Maybe[Any]'] #: Typesafe equality comparison with other `Result` objects. equals = container_equality
[docs] def map( self, function: Callable[[_ValueType], _NewValueType], ) -> 'Maybe[_NewValueType]': """ Composes successful container with a pure function. .. code:: python >>> from returns.maybe import Some, Nothing >>> def mappable(string: str) -> str: ... return string + 'b' >>> assert Some('a').map(mappable) == Some('ab') >>> assert Nothing.map(mappable) == Nothing """
[docs] def apply( self, function: Kind1['Maybe', Callable[[_ValueType], _NewValueType]], ) -> 'Maybe[_NewValueType]': """ Calls a wrapped function in a container on this container. .. code:: python >>> from returns.maybe import Some, Nothing >>> def appliable(string: str) -> str: ... return string + 'b' >>> assert Some('a').apply(Some(appliable)) == Some('ab') >>> assert Some('a').apply(Nothing) == Nothing >>> assert Nothing.apply(Some(appliable)) == Nothing >>> assert Nothing.apply(Nothing) == Nothing """
[docs] def bind( self, function: Callable[[_ValueType], Kind1['Maybe', _NewValueType]], ) -> 'Maybe[_NewValueType]': """ Composes successful container with a function that returns a container. .. code:: python >>> from returns.maybe import Nothing, Maybe, Some >>> def bindable(string: str) -> Maybe[str]: ... return Some(string + 'b') >>> assert Some('a').bind(bindable) == Some('ab') >>> assert Nothing.bind(bindable) == Nothing """
[docs] def bind_optional( self, function: Callable[[_ValueType], Optional[_NewValueType]], ) -> 'Maybe[_NewValueType]': """ Binds a function returning an optional value over a container. .. code:: python >>> from returns.maybe import Some, Nothing >>> from typing import Optional >>> def bindable(arg: str) -> Optional[int]: ... return len(arg) if arg else None >>> assert Some('a').bind_optional(bindable) == Some(1) >>> assert Some('').bind_optional(bindable) == Nothing """
[docs] def lash( self, function: Callable[[Any], Kind1['Maybe', _ValueType]], ) -> 'Maybe[_ValueType]': """ Composes failed container with a function that returns a container. .. code:: python >>> from returns.maybe import Maybe, Some, Nothing >>> def lashable(arg=None) -> Maybe[str]: ... return Some('b') >>> assert Some('a').lash(lashable) == Some('a') >>> assert Nothing.lash(lashable) == Some('b') We need this feature to make ``Maybe`` compatible with different ``Result`` like operations. """
def __iter__(self) -> Iterator[_ValueType]: """API for :ref:`do-notation`.""" yield self.unwrap()
[docs] @classmethod def do( cls, expr: Generator[_NewValueType, None, None], ) -> 'Maybe[_NewValueType]': """ Allows working with unwrapped values of containers in a safe way. .. code:: python >>> from returns.maybe import Maybe, Some, Nothing >>> assert Maybe.do( ... first + second ... for first in Some(2) ... for second in Some(3) ... ) == Some(5) >>> assert Maybe.do( ... first + second ... for first in Some(2) ... for second in Nothing ... ) == Nothing See :ref:`do-notation` to learn more. """ try: return Maybe.from_value(next(expr)) except UnwrapFailedError as exc: return exc.halted_container # type: ignore
[docs] def value_or( self, default_value: _NewValueType, ) -> Union[_ValueType, _NewValueType]: """ Get value from successful container or default value from failed one. .. code:: python >>> from returns.maybe import Nothing, Some >>> assert Some(0).value_or(1) == 0 >>> assert Nothing.value_or(1) == 1 """
[docs] def or_else_call( self, function: Callable[[], _NewValueType], ) -> Union[_ValueType, _NewValueType]: """ Get value from successful container or default value from failed one. Really close to :meth:`~Maybe.value_or` but works with lazy values. This method is unique to ``Maybe`` container, because other containers do have ``.alt`` method. But, ``Maybe`` does not have this method. There's nothing to ``alt`` in ``Nothing``. Instead, it has this method to execute some function if called on a failed container: .. code:: pycon >>> from returns.maybe import Some, Nothing >>> assert Some(1).or_else_call(lambda: 2) == 1 >>> assert Nothing.or_else_call(lambda: 2) == 2 It might be useful to work with exceptions as well: .. code:: pycon >>> def fallback() -> NoReturn: ... raise ValueError('Nothing!') >>> Nothing.or_else_call(fallback) Traceback (most recent call last): ... ValueError: Nothing! """
[docs] def unwrap(self) -> _ValueType: """ Get value from successful container or raise exception for failed one. .. code:: pycon :force: >>> from returns.maybe import Nothing, Some >>> assert Some(1).unwrap() == 1 >>> Nothing.unwrap() Traceback (most recent call last): ... returns.primitives.exceptions.UnwrapFailedError """ # noqa: RST307
[docs] def failure(self) -> None: """ Get failed value from failed container or raise exception from success. .. code:: pycon :force: >>> from returns.maybe import Nothing, Some >>> assert Nothing.failure() is None >>> Some(1).failure() Traceback (most recent call last): ... returns.primitives.exceptions.UnwrapFailedError """ # noqa: RST307
[docs] @classmethod def from_value( cls, inner_value: _NewValueType, ) -> 'Maybe[_NewValueType]': """ Creates new instance of ``Maybe`` container based on a value. .. code:: python >>> from returns.maybe import Maybe, Some >>> assert Maybe.from_value(1) == Some(1) >>> assert Maybe.from_value(None) == Some(None) """ return Some(inner_value)
[docs] @classmethod def from_optional( cls, inner_value: Optional[_NewValueType], ) -> 'Maybe[_NewValueType]': """ Creates new instance of ``Maybe`` container based on an optional value. .. code:: python >>> from returns.maybe import Maybe, Some, Nothing >>> assert Maybe.from_optional(1) == Some(1) >>> assert Maybe.from_optional(None) == Nothing """ if inner_value is None: return _Nothing(inner_value) return Some(inner_value)
@final class _Nothing(Maybe[Any]): """Represents an empty state.""" __slots__ = () _inner_value: None _instance: Optional['_Nothing'] = None def __new__(cls, *args: Any, **kwargs: Any) -> '_Nothing': if cls._instance is None: cls._instance = object.__new__(cls) # noqa: WPS609 return cls._instance def __init__(self, inner_value: None = None) -> None: # noqa: WPS632 """ Private constructor for ``_Nothing`` type. Use :attr:`~Nothing` instead. Wraps the given value in the ``_Nothing`` container. ``inner_value`` can only be ``None``. """ super().__init__(None) def __repr__(self): """ Custom ``str`` definition without the state inside. .. code:: python >>> from returns.maybe import Nothing >>> assert str(Nothing) == '<Nothing>' >>> assert repr(Nothing) == '<Nothing>' """ return '<Nothing>' def map(self, function): """Does nothing for ``Nothing``.""" return self def apply(self, container): """Does nothing for ``Nothing``.""" return self def bind(self, function): """Does nothing for ``Nothing``.""" return self def bind_optional(self, function): """Does nothing.""" return self def lash(self, function): """Composes this container with a function returning container.""" return function(None) def value_or(self, default_value): """Returns default value.""" return default_value def or_else_call(self, function): """Returns the result of a passed function.""" return function() def unwrap(self): """Raises an exception, since it does not have a value inside.""" raise UnwrapFailedError(self) def failure(self) -> None: """Returns failed value.""" return self._inner_value
[docs]@final class Some(Maybe[_ValueType]): """ Represents a calculation which has succeeded and contains the value. Quite similar to ``Success`` type. """ __slots__ = () _inner_value: _ValueType def __init__(self, inner_value: _ValueType) -> None: """Some constructor.""" super().__init__(inner_value) if not TYPE_CHECKING: # noqa: WPS604 # pragma: no branch
[docs] def bind(self, function): """Binds current container to a function that returns container.""" return function(self._inner_value)
[docs] def bind_optional(self, function): """Binds a function returning an optional value over a container.""" return Maybe.from_optional(function(self._inner_value))
[docs] def unwrap(self): """Returns inner value for successful container.""" return self._inner_value
[docs] def map(self, function): """Composes current container with a pure function.""" return Some(function(self._inner_value))
[docs] def apply(self, container): """Calls a wrapped function in a container on this container.""" if isinstance(container, Some): return self.map(container.unwrap()) # type: ignore return container
[docs] def lash(self, function): """Does nothing for ``Some``.""" return self
[docs] def value_or(self, default_value): """Returns inner value for successful container.""" return self._inner_value
[docs] def or_else_call(self, function): """Returns inner value for successful container.""" return self._inner_value
[docs] def failure(self): """Raises exception for successful container.""" raise UnwrapFailedError(self)
#: Public unit value of protected :class:`~_Nothing` type. Nothing: Maybe[NoReturn] = _Nothing() Maybe.empty = Nothing
[docs]def maybe( function: Callable[_FuncParams, Optional[_ValueType]], ) -> Callable[_FuncParams, Maybe[_ValueType]]: """ Decorator to convert ``None``-returning function to ``Maybe`` container. This decorator works with sync functions only. Example: .. code:: python >>> from typing import Optional >>> from returns.maybe import Nothing, Some, maybe >>> @maybe ... def might_be_none(arg: int) -> Optional[int]: ... if arg == 0: ... return None ... return 1 / arg >>> assert might_be_none(0) == Nothing >>> assert might_be_none(1) == Some(1.0) """ @wraps(function) def decorator( *args: _FuncParams.args, **kwargs: _FuncParams.kwargs, ) -> Maybe[_ValueType]: return Maybe.from_optional(function(*args, **kwargs)) return decorator