Last active
April 11, 2019 06:26
-
-
Save kendricktan/bb0c8063a59dbcb2397ef8fe05195b88 to your computer and use it in GitHub Desktop.
Maybe Monads in Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from abc import ABC, abstractmethod | |
| class Applicative(ABC): | |
| @abstractmethod | |
| def fmap(self, f): | |
| raise Exception('Not implemented!') | |
| class Functor(ABC): | |
| @abstractmethod | |
| def apply(self, f): | |
| raise Exception('Not implemented!') | |
| class Monad(ABC): | |
| @abstractmethod | |
| def bind(self, f): | |
| raise Exception('Not implemented!') | |
| # Maybe Monad | |
| class Maybe( | |
| Applicative, | |
| Functor, | |
| Monad | |
| ): | |
| def __init__(self, value): | |
| self.value = value | |
| def bind(self, f): | |
| """ | |
| bind :: (a -> m b) -> m a -> m b | |
| Param: | |
| f :: (a -> m b) | |
| """ | |
| if self.value is None: | |
| return Maybe(None) | |
| value = self.value | |
| return f(value) | |
| def apply(self, ff): | |
| """ | |
| apply :: f (a -> b) -> f a -> f b | |
| Param: | |
| ff :: f (a -> b) | |
| """ | |
| f = ff.value | |
| return self.fmap(f) | |
| def fmap(self, f): | |
| """ | |
| fmap :: (a -> b) -> m a -> m b | |
| Param: | |
| f :: a -> b | |
| """ | |
| if self.value == None: | |
| return Maybe(None) | |
| new_value = f(self.value) | |
| return Maybe(new_value) | |
| def eval(self, v): | |
| if callable(self.value): | |
| return self.value(v) | |
| return self.value | |
| def __str__(self): | |
| if self.value == None: | |
| return 'Nothing' | |
| return f'Just {self.value}' | |
| def pure(v): | |
| return Maybe(v) | |
| def createMaybeInt(v): | |
| if v < 0: | |
| return pure(None) | |
| return pure(v) | |
| def minus(y): return lambda x: x - y | |
| minusOne = minus(1) | |
| minusTwo = minus(2) | |
| def plus(y): return lambda x: x + y | |
| plusOne = plus(1) | |
| plusTwo = plus(2) | |
| def noneIfLEZero(x): return pure(x) if x > 0 else pure(None) | |
| # Just an Int | |
| justAnInt = pure(42) \ | |
| .fmap(plus(10)) \ | |
| .bind(lambda x: pure (x + 1)) | |
| print(justAnInt) | |
| # Produces Nothing | |
| noneInt = pure(10) \ | |
| .fmap(minus(50)) \ | |
| .bind(noneIfLEZero) | |
| print(noneInt) | |
| # Still able to perform arbitrary logic | |
| # Maybe monad without explicitly introducing | |
| # it into the control flow | |
| maybeNoneInt = pure(10) \ | |
| .fmap(minus(50)) \ | |
| .bind(noneIfLEZero) \ | |
| .fmap(plus(10)) | |
| print(maybeNoneInt) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment