From Chen's Wiki

Jump to: navigation, search

Contents

Keywords

This post may help you go through the following tips:

  1. some concepts of monads
  2. Python function decorator
  3. Python operators
  4. Class inheritance

Although I am good at none of them.

The ">>=" operator

The ">>=" infix operator is the real sugar in Haskell monad application. Looking at implementations of other languages, they are either prefixed or parathensis required or even both, like Python. e.g. bind(value, lambda x: ......) More frustrating for chaining.

Python does not support operator definition (I might misuse the term. what I mean is ...) nor creation of infix functions. So is it possible to make a counterpart? Luckily, Python do support some sort of operator overloading in a class definition.

Challenges

  1. Suppose we can implement the operator ">>=", as a fact of using Python, the both sides of the operator should have the same class type. Which implies that in "some_value >>= function", some_value should be in the same class (or a subclass, this is vital!) as the function.
  2. There is no predefined ">>=" operator, the only possible way of making it is by composing/chaining "__gt__" and "__ge__". However, they cannot be chained because both of them are binary operators and the there should be an object in between. So one of the two has to be unary. The unary operators are limited in number. So I have to pick "__invert__" and then "__rshift__" to make ">>~". Well, I call it equally good!

Attacking the problems

1. Then let's make both the same type: Monad! For the right side, the function, I use Python decorator syntax to make the later application more formal.

class Monad(object):
        def __init__(self, function=lambda x: x):
                self.function = function

        def __rshift__(self):
                raise NotImplementedError

        def __invert__(self):
                return self

def monadic(f):
        return Monad(f)

2. To illustrate, I implemented a Maybe monad in my paradigm. There comes the juice.

from monad import *

class Maybe(Monad):     
        def __init__(self, value):
                super(Maybe, self).__init__()
                self.value = value

        def __rshift__(self, other):
                return other.function(self.value)

        def __lshift__(self, other):
                return self.value

        def __repr__(self):
                if self.value == None: return 'Nothing'
                else: return 'Just(%r)' % self.value
        
        @classmethod
        def unit(cls, value):
                return cls(value)

#unit is the Haskell return
unit = Maybe.unit

You can see how ">>" gets chained by "~" to form ">>~" to mimic ">>=".

Testing

By using the decorator syntax, it is acceptable faking the class type instead of telling the user explicitly. Tada, I implemented the Maybe monad in Python with relatively eye-candy syntax. (Sure, it may not be strictly a Monad but I just wanna show how to work around the critial part.)

@monadic
def f(x): return unit(x+1)

@monadic
def g(x): return unit(x+2)

m = Maybe(1)
n = Maybe(None)

#showing the Haskell >>= operator
res = m >>~ f >>~ g
print res
#emit -> Just(4)

#showing the Haskell >> operator
res = n << f
print res
#emit -> Nothing

Source files can be found here.

Comments


Anonymous User #1

318 days ago
Score 0+

very nice work!

i have a suggestion: do the wrapping of the return value inside the monadic decorator. this would allow to apply the monadic decorator to "normal" functions
Add your Comment
Chen's Wiki welcomes all comments. If you don't want to be anonymous, register or log in. It's free.