Source code for smonad.types.ftry

# -*- coding: utf-8 -*-
# Copyright (c) 2012-2014, Philip Xu <pyx@xrefactor.com>
# License: BSD New, see LICENSE for details.
"""smonad.types.ftry - The Try Monad."""

from . import Monad, Monadic
from ..mixins import ContextManager, Ord


[docs]class Try(Monad, ContextManager, Ord): """The Either Monad by a different name Represents values/computations with two possibilities. >>> Success(42) Success(42) >>> Success([1, 2, 3]) Success([1, 2, 3]) >>> Failure('Error') Failure('Error') >>> Success(Failure('Error')) Success(Failure('Error')) >>> isinstance(Success(1), Try) True >>> isinstance(Failure(None), Try) True >>> saving = 100 >>> broke = Failure('I am broke') >>> spend = lambda cost: broke if cost > saving else Success(saving - cost) >>> spend(90) Success(10) >>> spend(120) Failure('I am broke') >>> safe_div = lambda a, b: Failure(str(a) + '/0') if b == 0 else Success(a / b) >>> safe_div(12.0, 6) Success(2.0) >>> safe_div(12.0, 0) Failure('12.0/0') Bind operation with ``>>`` >>> inc = lambda n: Success(n + 1) if type(n) is int else Failure('Type error') >>> Success(0) Success(0) >>> Success(0) >> inc Success(1) >>> Success(0) >> inc >> inc Success(2) >>> Success('zero') >> inc Failure('Type error') Comparison with ``==``, as long as they are the same type and what's wrapped inside are comparable. >>> Failure(42) == Failure(42) True >>> Success(42) == Success(42) True >>> Failure(42) == Success(42) False A :py:class:`Failure` is less than a :py:class:`Success`, or compare the two by the values inside if thay are of the same type. >>> Failure(42) < Success(42) True >>> Success(0) > Failure(100) True >>> Failure('Error message') > Success(42) False >>> Failure(100) > Failure(42) True >>> Success(-2) < Success(-1) True """ def __init__(self, value): super(Try, self).__init__(value) if type(self) is Try: raise NotImplementedError('Please use Failure or Success instead')
[docs] def bind(self, function): """The bind operation of :py:class:`Try`. Applies function to the value if and only if this is a :py:class:`Success`. """ return self and function(self.value)
def __repr__(self): """Customize Show.""" fmt = 'Success({})' if self else 'Failure({})' return fmt.format(repr(self.value)) # Customize Ord logic def __lt__(self, monad): """Override to handle special case: Success.""" if not isinstance(monad, (Failure, Success)): fmt = "unorderable types: {} and {}'".format raise TypeError(fmt(type(self), type(monad))) if type(self) is type(monad): # same type, either both lefts or rights, compare against value return self.value < monad.value if monad: # self is Failure and monad is Success, left is less than right return True else: return False
[docs] def match(self, failure_handler, right_handler=None): """ Given two mapping functions (one from an Failure to a value, one from a Success to a Value), unwrap the value stored in this Try, apply the appropriate mapping function, and return the result. If the right_handler is None and self is an instance of Success, the wrapped value is returned """ if isinstance(self, Failure): return failure_handler(self.value) if right_handler is None: return self.value return right_handler(self.value)
[docs] def recover(self, recovery_fn): """ "Recover" from a left value by applying a recovery_fn to the wrapped value and returning it in the case of a left value; otherwise, return the wrapped right value. """ if isinstance(self, Failure): return recovery_fn(self.value) else: return self
[docs]class Failure(Try): """Failure of :py:class:`Try`.""" def __bool__(self): # pylint: disable = no-self-use return False __nonzero__ = __bool__
[docs]class Success(Try): """Success of :py:class:`Try`."""
Try.unit = Monadic(Success)