However, I am curious if python's solution looks ugly (since they don't seem to have a 'do' or 'for' expression that Haskell and Scala have respectively)?
However, I am curious if python's solution looks ugly (since they don't seem to have a 'do' or 'for' expression that Haskell and Scala have respectively)?
The benefit of realising the monadic behaviour of asynchronous sequencing (which I call "Deferrable#bind!") was that if you had a bunch of nested callbacks, the monad associativity law [2] meant you could replace them with a simple linear sequence of chained bind! calls:
fetch().bind! do |a|
process(a)
end.bind! do |b|
process(b)
end.bind! do |c|
# ...
end
instead of fetch().callback do |a|
process(a).callback do |b|
process(b).callback do |c|
# ...
end
end
end
I find the former a lot more readable, partly because the "end" keywords don't all pile up at the end, and especially if you try and do error handling (in the nested case the error handling ends up in reverse order!).[1] https://github.com/samstokes/deferrable_gratification#bind-f...
They do stuff like this in jQuery, so I can't imagine it's all that difficult in Python.
My initial thought would be to write something analogous to Maybe. You can't pattern match in Python, which is a shame, but you could make it so that foo.bar().baz().quux() will return MaybeResult, which everything involved in the transaction inherits from. MaybeResult might have a "success" variable on it indicating whether it ought to be treated as Nothing. Otherwise, check "result."
This is naive but it gets you a little bit closer to fault-tolerant chaining of interdependent methods. Not as nice as Haskell, obvs. :)
login_action = Write('PASS') >> \
Read('pass') >> \
Guard(lambda v: v['pass'] == 'mysecret') >> \
Write('WELCOME')
register(socket, login_action, on_success=func1, on_error=func2)
Of course you can develop this idea futher: cat = Read('line') >> Write(lambda v: v['line'])
cat.append_action(cat)
register(socket, cat, on_error=handle_error_cb)