Skip to content

Instantly share code, notes, and snippets.

@bgnori
Last active December 29, 2015 01:49
Show Gist options
  • Save bgnori/7595741 to your computer and use it in GitHub Desktop.
Save bgnori/7595741 to your computer and use it in GitHub Desktop.
itertoolsとcollections, functoolsの勉強が必要. 無限リストの扱いの都合上、pythonのlistをそのまま使うことはできない.
#!/usr/bin/python
# -*- coding=utf8 -*-
"""
kana notation: usint | for bind of monad, flatMap in Scala.
see http://labs.timedia.co.jp/2011/05/monad-in-python.html
need monad law test
a. Associativity
(m flatMap f) flatMap g == m flatMap (f flatMap g)
(m | f ) | g = m | (f | g)
b. left unit
unit(x) flatMap f == f(x)
unit(x) | f = f(x)
c. right unit
m flatMap unit = m
m | unit == m
need wrapper for list, set
need wrapper for function.
i.e.
in Scala
m map f == m flatMap ( x => unit(f(x))) == m flatMap (f andThen unit)
in kana notation with andThen defined as function with closure
m.map(f) == m | andThen(f, unit)
compose/andThen in Scala
f = h compose g
f = h(g(x))
f = h andThen g
f = g(h(x))
"""
from itertools import *
import collections
class Monad:
def __init__(self, x):
self.value = x
def bind(self, f):
"""
f returns M[U] from T, Do I need inspect f?
flatMap in Scala
| in kana notation
Mustbe overridden"""
raise NotImplementedError
def __or__(self, a_to_m_b):
""" bind is | in kana notation """
return self.bind(a_to_m_b)
def __repr__(self):
return "%s(%s)"%(self.__class__.__name__, self.value,)
class Maybe(Monad):
def bind(self, f):
if isinstance(self, Nothing):
return self
else:
return f(self.value)
class Just(Maybe):
"""
>>> import functools
>>> curry = functools.partial
>>> def flip(f):
... return lambda x, y: f(y, x)
...
>>> def safe_div(dividend, divisor):
... if divisor == 0:
... return Nothing
... else:
... return Just(dividend / divisor)
...
>>> safe_rdiv = flip(safe_div)
>>> Just(4) | curry(safe_rdiv, 2)
Just(2)
"""
class Nothing(Maybe):
"""
wannat use None, it is more natural as python
>>> import functools
>>> curry = functools.partial
>>> def flip(f):
... return lambda x, y: f(y, x)
...
>>> def safe_div(dividend, divisor):
... if divisor == 0:
... return Nothing(None)
... else:
... return Just(dividend / divisor)
...
>>> safe_rdiv = flip(safe_div)
>>> Just(4) | curry(safe_rdiv, 0)
Nothing
>>> Just(4) | curry(safe_rdiv, 0) | curry(safe_rdiv, 5)
Nothing
"""
def __repr__(self):
return "Nothing"
class Try(Monad):
#>>> Try(4) | curry(unsafe_rdiv, 0)
"""
>>> import functools
>>> curry = functools.partial
>>> def flip(f):
... return lambda x, y: f(y, x)
...
>>> def unsafe_div(dividend, divisor):
... return dividend / divisor
>>> unsafe_rdiv = flip(unsafe_div)
>>> Try("4/0").run()
Failue(integer division or modulo by zero)
>>> Try("4/2").run()
Success(2)
>>> Try(lambda : 4/2).run()
Success(2)
>>> Try(lambda : 4/0).run()
Failue(integer division or modulo by zero)
"""
def run(self):
""" word 'apply' is not vacant for us"""
try:
if isinstance(self.value, str):
return Success(eval(self.value))
return Success(self.value())
except Exception, ex:
return Failue(ex)
def bind(self, f):
if isinstance(self, Success):
return self.run()
else:
return self
class Success(Try):
pass
class Failue(Try):
pass
class MonadicList(Monad):
"""maybe to be parametric, want it be metaclass,
i.e.
monadicList = unitof(list)
monadicSet = unitof(set)
etc.
>>> import functools
>>> curry = functools.partial
>>> ml = MonadicList((1, 2, 3))
>>> ml
MonadicList(1, 2, 3)
>>> ml | (lambda x : x +1)
MonadicList(2, 3, 4)
"""
def __init__(self, x):
if isinstance(x, collections.Iterable):
self.value = x
else:
self.value = (x, )
def bind(self, f):
return MonadicList(f(x) for x in self.value)
def map(self, f):
return map(f, self.value)
def __repr__(self):
return "%s(%s)"%(self.__class__.__name__,
", ".join(map(str, islice(self.value, 3))))
def andThen(f, g):
def foo(*args, **kw):
return g(f(*args, **kw))
return foo
if __name__ == "__main__":
import doctest
doctest.testmod()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment