title: Error monad in Python - did I do it right?
body: <p>Questions:</p>
  
  <ul>
  <li>Is this a monad? </li>
  <li>does this demonstrate a reasonable understanding of error monad? </li>
  <li>what am I missing?</li>
  <li>what else can I do with this code to flex monads more?</li>
  <li>i'm confused as to the relation of success/fail to "return"/"result"/"lift" (i think these are all the same concepts).</li>
  <li>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 <code>if result != None</code> plumbing, what other types of plumbing might I want to abstract, and how do monads (or 'monad combinators') help this pain?</li>
  </ul>
  
  <p>I'm a bit underwhelmed.</p>
  
  <pre><code># 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 &gt; 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)
  </code></pre>
  
  <p>output: </p>
  
  <pre><code>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'")
  </code></pre>