4 ms·
I was learning Haskell for last six months (part of my undergraduate studies). It was (and is!) fun and challenging but I had hard time seeing any measurable p
by sznurek 14y ago
I was learning Haskell for last six months (part of my undergraduate studies).
It was (and is!) fun and challenging but I had hard time seeing any measurable profits from learning what are and how to use functors, monads etc.
Until recently when I was writing asynchronous communication framework in Python and I was searching for simple solution to sequence actions in this heavily callback-based environment.
Now everything looks so simple... (I am looking at you, monads!)
The bad part is that many people do not know what I am talking about when I try to describe them the design ;)
- runT1ME 14y agoI had the exact same experience! A light went off when I figured out how to elegantly sequence asynchronous calls in Scala, and it was then that I realized I was using Monads to do so. 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)?
- wonderzombie 14y agoI'm guessing you can't really do a 1:1, although maybe the OP can clarify. 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. :)
- sznurek 14y agoI wrote 'monad-inspired' structure because of lack of syntactic sugar (do notation). So it basically looks like that: 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)
- edwkar 14y agoNice!!
- samstokes 14y agoI did the same thing in Ruby [1], but it does indeed look uglier than the equivalent Haskell/Scala because of the lack of language support for monadic syntax. 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-for-when-one-thing-leads-to-another https://github.com/samstokes/deferrable_gratification#bind-f... [2] http://www.haskell.org/haskellwiki/Monad_Laws http://www.haskell.org/haskellwiki/Monad_Laws
- jerf 14y agoGo check out gevent. It removes the need for heavily call-back dependent code.
- tikhonj 14y agoI actually had a very similar revelation in JavaScript. Using Haskell-style arrows to abstract over things like events and CPS functions is really great for that sort of code. The real beauty of these concepts is that they are not tied to just one logical thing. They're applicable to callbacks and networking, but then they translate effortlessly to handling errors or early termination or non-determinism.