Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maybe monad in Python with method chaining

I'm trying to implement the Maybe monad in python. However what I also want is some kind of a chaining ability.

So I have a class:

class Maybe:
    def __init__(self, val):
        self.val = val

    def do(self, func):  # Bind function
        if self.val is None:
            return None
        else:
            return func(self.val)

I have two functions :

def double(number):
    try:
        result = number * 2
        return Maybe(result)
    except:
        return Maybe(None)

def square(number):
    try:
        result = number * number
        return Maybe(result)
    except:
        return Maybe(None)

Here's how I'm using this :

 result = Maybe(5).do(double).do(square)
    print(result.val)

I'm looking at a way to chain multiple functions each performing a specific task. Each function takes the output of the previous function as input. The chain should break if any function in the chain throws an exception.

Is this the right way to model the Maybe monad ?

Is this the right way to handle exceptions too ?

Can this be improved upon ?

Thanks a lot.

like image 223
lazy python Avatar asked Feb 19 '15 13:02

lazy python


1 Answers

The downside to this is that it intentionally suppresses errors, which is generally considered a bad idea in python.

However, you can catch and store any errors occur in your Maybe instance and report them back.

For example:

class Maybe(object):
    def __init__(self, val, error=None):
        self.val = val
        self.error = error

    def __repr__(self):
        if self.val is not None:
            return repr(self.val)
        else:
            return repr(self.error)

    def do(self, func):
        if self.val is None:
            return self
        try:
            return Maybe(func(self.val))
        except Exception as e:
            return Maybe(None, e)

def squared(x):
    return x * x

def addone(x):
    return x + 1

result1 = Maybe(5).do(squared).do(addone)
result2 = Maybe('a').do(squared).do(addone)
print result1
print result2

This yields:

26
TypeError("can't multiply sequence by non-int of type 'str'",)

This is similar to DanD's answer, but has the advantage of storing the error that occurred instead of completely suppressing it.

No matter how you slice it, this idiom is going to feel somewhat "unpythonic", but this is a slightly more robust way of handling it.

like image 59
Joe Kington Avatar answered Oct 19 '22 14:10

Joe Kington