Skip to content

Instantly share code, notes, and snippets.

@jordanyaker
Created October 5, 2017 18:10
Show Gist options
  • Save jordanyaker/2850956fea18a3f3b65b13ef0c0cbe7d to your computer and use it in GitHub Desktop.
Save jordanyaker/2850956fea18a3f3b65b13ef0c0cbe7d to your computer and use it in GitHub Desktop.
Try-Monad
# ------------------------------------------------------------
# Based on Jason DeLaat's Either monad:
# https://github.com/fnl/pymonad/blob/master/pymonad/Either.py
# ------------------------------------------------------------
from pymonad.Monad import *
class Try(Monad):
"""
Represents an operation that may either fail or succeed.
An alternative to using exceptions. 'Try' is an abstract type and should not
be instantiated directly. Instead use 'Success' and 'Failure'.
"""
def __init__(self, value):
""" Raises a 'NotImplementedError'. Use 'Success' or 'Failure' instead. """
raise NotImplementedError
def __eq__(self, other):
if not isinstance(other, Try): raise TypeError("Can't compare different types.")
@classmethod
def unit(cls, value):
return Success(value)
@property
def is_success(self):
return False
@property
def is_failure(self):
return False
class Failure(Try):
"""
Represents an operation which has failed and contains an error code or message.
To help with readaility you may alternatively use the alias 'Error'.
"""
def __init__(self, errorMsg):
"""
Creates a 'Failure' "operation failed" object.
'errorMsg' can be anything which gives information about what when wrong.
"""
super(Try, self).__init__(errorMsg)
def __eq__(self, other):
super(Failure, self).__eq__(other)
if not isinstance(other, Failure):
return False
elif self.getValue() == other.getValue():
return True
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
def __str__(self):
return "Failure: " + str(self.getValue())
def fmap(self, _):
""" Returns the 'Failure' instance that was used to call the method. """
return self
def amap(self, _):
""" Returns the 'Failure' instance that was used to call the method. """
return self
def bind(self, _):
""" Returns the 'Failure' instance that was used to call the method. """
return self
@property
def is_failure(self):
return True
class Success(Try):
"""
Represents an operation which has succeeded and contains the result of that operation.
"""
def __init__(self, value):
"""
Creates a 'Success' "operation succeeded" object.
'value' is the actual calculated value of whatever operation was being performed
and can be any type.
"""
super(Try, self).__init__(value)
def __eq__(self, other):
super(Success, self).__eq__(other)
if not isinstance(other, Success):
return False
elif self.getValue() == other.getValue():
return True
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
def __str__(self):
return "Success: " + str(self.getValue())
def fmap(self, function):
"""
Applies 'function' to the contents of the 'Success' instance and returns a
new 'Success' object containing the result.
'function' should accept a single "normal" (non-monad) argument and return
a non-monad result.
"""
return Success(function(self.getValue()))
def amap(self, functor_value):
"""Applies the function stored in the functor to 'functor_value'.
Args:
functor_value: A function to be applied.
Returns:
pymonad.Monad.Try: A new Try value.
"""
return self.getValue() * functor_value
def bind(self, function):
"""
Applies 'function' to the result of a previous operation.
'function' should accept a single "normal" (non-monad) argument and return
either a 'Failure' or 'Success' type object.
"""
return function(self.getValue())
@property
def is_success(self):
return True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment