我正在尝试编写 seq-m 和 error-m 来对可能返回错误的事物进行列表理解。我的输出有意想不到的类型,尽管除此之外它实际上似乎是明智的。我已经在下面分解了我的代码,但这里也是一个工作要点。
这是我的一元业务逻辑
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)
输出
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')
我希望看到元组列表 - 该列表是列表理解的结果,最终列表中的每个项目都应该是错误单子(value, error
元组)中的一个值。就像删除了太多的嵌套级别一样seq_bind
。
这是我对 monad 的定义,如果它不正确,那么它非常接近,因为两个 monad 都是孤立地工作的,而不是结合起来的。
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))
一元API
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 > 200000:
return success([balance])
else:
return error("Insufficient funds for loan, current balance is %s" % balance)
也在寻找改进代码的方法。标记 haskell 和 clojure 因为这是这些语言的惯用语,python 社区对此不感兴趣。