4

Questions:

- Is this a monad?
- does this demonstrate a reasonable understanding of error monad?
- what am I missing?
- what else can I do with this code to flex monads more?
- i'm confused as to the relation of success/fail to "return"/"result"/"lift" (i think these are all the same concepts).
- how can we make the problem more complex, such that monads help us solve our pain points? monads help here because we abstracted away the
`if result != None`

plumbing, what other types of plumbing might I want to abstract, and how do monads (or 'monad combinators') help this pain?

I'm a bit underwhelmed.

```
# helpers for returning error codes
def success(x): return (True, x)
def fail(x): return (False, x)
# bind knows how to unwrap the return value and pass it to
# the next function
def bind(mv, mf):
succeeded = mv[0]
value = mv[1]
if (succeeded): return mf(value)
else: return mv
def lift(val): return success(val)
def userid_from_name(person_name):
if person_name == "Irek": return success(1)
elif person_name == "John": return success(2)
elif person_name == "Alex": return success(3)
elif person_name == "Nick": return success(1)
else: return fail("No account associated with name '%s'" % person_name)
def balance_from_userid(userid):
if userid == 1: return success(1000000)
elif userid == 2: return success(75000)
else: return fail("No balance associated with account #%s" % userid)
def balance_qualifies_for_loan(balance):
if balance > 200000: return success(balance)
else: return fail("Insufficient funds for loan, current balance is %s" % balance)
def name_qualifies_for_loan(person_name):
"note pattern of lift-bind-bind-bind, we can abstract further with macros"
mName = lift(person_name)
mUserid = bind(mName, userid_from_name)
mBalance = bind(mUserid, balance_from_userid)
mLoan = bind(mBalance, balance_qualifies_for_loan)
return mLoan
for person_name in ["Irek", "John", "Alex", "Nick", "Fake"]:
qualified = name_qualifies_for_loan(person_name)
print "%s: %s" % (person_name, qualified)
```

output:

```
Irek: (True, 1000000)
John: (False, 'Insufficient funds for loan, current balance is 75000')
Alex: (False, 'No balance associated with account #3')
Nick: (True, 1000000)
Fake: (False, "No account associated with name 'Fake'")
```

I restructured that code slightly to make it more readable. Hope you don't mind (and if you do, please roll it back : - Niklas B. 2012-04-04 02:07

2

**Is this a monad?** See the monad laws:

All instances of the Monad class should obey:

- "Left identity": return a >>= f ≡ f a
- "Right identity": m >>= return ≡ m
- "Associativity": (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)

*( return means success, >>= means bind)*

Left identity. In your implementation, this could be:

`bind(success(x), balance_qualifies_for_loan) == balance_qualifies_for_loan(x)`

where

`x`

is some value and`f`

is a monadic function.Right identity. Again, this could be:

`bind(m, success) == m`

where

`m`

is a monadic value.Associativity. This could be:

`bind(bind(m, userid_from_name), balance_from_userid)) == bind(m, lambda x: bind(userid_from_name(x), balance_from_userid))`

All of these could be written as unit tests to quickly check that these properties hold for many input values.

**What is missing?**

- each monad needs a different implementation of
`success`

and`bind`

. Putting these into an interface would allow you to write code generic over all implemented monads. - based on the Haskell approach, you might want to implement some general monad combinators, such as
`>>`

,`sequence`

and`mapM`

. These make monads very convenient to use.

can you extend my problem such that monad combinators help me solve it? I guess I'm trying to understand what sort of pain points one might have that monads help solve - Dustin Getz 2012-04-04 02:17

`>>`

operator and using lambdas. A totally different approach is the one from here, that guy uses decorators and`yield`

to make this almost look right : - Niklas B. 2012-04-04 02:02