title: combining maybe and seq monads: confused at the output
body: <p>I'm trying to compose seq-m and error-m to do list comprehensions on things that can return errors. My output has unexpected types, though other than that it does actually seem to be sensible. i've exploded my code below, but here is a <a href="https://github.com/dustingetz/sandbox/blob/master/etc/monads/loan.py" rel="noreferrer">working gist</a> as well.</p>
  
  <p>here is my monadic business logic</p>
  
  <pre><code>def get_loan(name):
      m_qualified_amounts = (
             bind(get_banks(name), lambda bank:
             bind(get_accounts(bank, name), lambda account:
             bind(get_balance(bank, account), lambda balance:
             bind(get_qualified_amount(balance), lambda qualified_amount:
                      unit(qualified_amount))))))
      return m_qualified_amounts
  
  names = ["Irek", "John", "Alex", "Fred"]
  for name, loans in zip(names, map(get_loan, names)):
      print "%s: %s" % (name, loans)
  </code></pre>
  
  <p>output</p>
  
  <pre><code>Irek: [None, 'Insufficient funds for loan, current balance is 35000', None, 'Insufficient funds for loan, current balance is 70000', None, 'Unable to get balance due to technical issue for Wells Fargo: 3']
  John: [None, 'Insufficient funds for loan, current balance is 140000']
  Alex: [[245000], None, [280000], None]
  Fred: (None, 'No bank associated with name Fred')
  </code></pre>
  
  <p>i expect to see lists of tuples - the list is the result of the list comprehension, and each item in the final list should be a value in error-monad (<code>value, error</code> tuple). Its exactly as if one-too-many levels of nesting were removed by <code>seq_bind</code>.</p>
  
  <p>here is my definition of the monads, which if its not correct, its very close because both monads work in isolation, just not combined.</p>
  
  <pre><code>def success(val): return val, None
  def error(why): return None, why
  def get_value(m_val): return m_val[0]
  def get_error(m_val): return m_val[1]
  
  # error monad
  def error_unit(x): return success(x)
  def error_bind(mval, mf):
      assert isinstance(mval, tuple)
      error = get_error(mval)
      if error: return mval
      else: return mf(get_value(mval))
  
  def flatten(listOfLists):
      "Flatten one level of nesting"
      return [x for sublist in listOfLists for x in sublist]    
  
  # sequence monad
  def seq_unit(x): return [x]
  def seq_bind(mval, mf):
      assert isinstance(mval, list)
      return flatten(map(mf, mval))
  
  # combined monad !!
  def unit(x): return error_unit(seq_unit(x))
  def bind(m_error_val, mf):  
      return error_bind(m_error_val, lambda m_seq_val: seq_bind(m_seq_val, mf))
  </code></pre>
  
  <p>monadic API</p>
  
  <pre><code>def get_banks(name):
      if name == "Irek": return success(["Bank of America", "Wells Fargo"])
      elif name == "John": return success(["PNC Bank"])
      elif name == "Alex": return success(["TD Bank"])
      else: return error("No bank associated with name %s" % name)
  
  def get_accounts(bank, name):
      if   name == "Irek" and bank == "Bank of America": return success([1, 2])
      elif name == "Irek" and bank == "Wells Fargo": return success([3])
      elif name == "John" and bank == "PNC Bank": return success([4])
      elif name == "John" and bank == "Wells Fargo": return success([5, 6])
      elif name == "Alex" and bank == "TD Bank": return success([7, 8])
      else: return error("No account associated with (%s, %s)" % (bank, name))
  
  def get_balance(bank, account):
      if bank == "Wells Fargo":
          return error("Unable to get balance due to technical issue for %s: %s" % (bank, account))
      else:
          return success([account * 35000])  #right around 200,000 depending on acct number
  
  def get_qualified_amount(balance):
      if balance &gt; 200000:
          return success([balance])
      else:
          return error("Insufficient funds for loan, current balance is %s" % balance)
  </code></pre>
  
  <p>also looking for ways to improve the code. tagged haskell and clojure because this is idiomatic in these languages, the python community isn't interested in this.</p>