Last active
February 21, 2016 12:42
-
-
Save kdungs/7c08fbc7e97fd4505459 to your computer and use it in GitHub Desktop.
Define a Check monad and corresponding functions in Python. Now also a repo: https://github.com/kdungs/python-mcheck
This file contains 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
""" Define a Check monad and corresponding functions. | |
""" | |
from functools import partial | |
class Check: | |
""" This super class is not really necessary but helps make the structure | |
clear. | |
data Check a = Pass a | Fail Message | |
""" | |
pass | |
class Pass(Check): | |
def __init__(self, value): | |
self.value = value | |
class Fail(Check): | |
def __init__(self, message): | |
self.message = message | |
def is_(t, x): | |
""" Check whether the type of a given x is a given type t. | |
""" | |
return type(x) is t | |
is_check = partial(is_, Check) | |
is_pass = partial(is_, Pass) | |
is_fail = partial(is_, Fail) | |
def return_(x): | |
""" Monadic return for the Check monad. | |
return :: a -> m a | |
return = Pass | |
""" | |
return Pass(x) | |
def bind(f): | |
""" Monadic bind for the Check monad. | |
(>>=) :: m a -> (a -> m b) -> m b | |
Fail x >>= f = Fail x | |
Pass x >>= f = f x | |
""" | |
def bind_impl(x): | |
if is_fail(x): | |
return x | |
if is_pass(x): | |
return f(x.value) | |
raise ValueError('Check has to be of type Pass | Fail.') | |
return bind_impl | |
def compose(f, g): | |
""" Kleisli composition of two (Check-)monadic functions f and g. | |
(>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c) | |
""" | |
def compose_impl(x): | |
return bind(g)(f(x)) | |
return compose_impl | |
def lift(f, message): | |
""" Lifts a boolean function into the realm of the Check monad. | |
lift :: (a -> bool) -> (a -> Check a) | |
""" | |
def lift_impl(x): | |
if f(x): | |
return return_(x) | |
return Fail(message) | |
return lift_impl |
This file contains 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
import check as c | |
def is_positive(x): | |
return x > 0 | |
def is_even(x): | |
return not x & 1 | |
check_positive = c.lift(is_positive, 'Number has to be positive.') | |
check_even = c.lift(is_even, 'Number has to be even.') | |
check_evenpositive = c.compose(check_even, check_positive) | |
print(check_positive(-5).message) | |
print(check_positive(5).value) | |
print(check_even(1).message) | |
print(check_even(2).value) | |
print(check_evenpositive(1).message) | |
print(check_evenpositive(2).value) | |
print(check_evenpositive(-1).message) | |
print(check_evenpositive(-2).message) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment