<i></i><i>While cringing at the cliche: monads were the key ingredient.</i><i></i><p>Let's not turn this into a cliche! Monads are powerful. I find do notation much easier to read in many cases:<p><pre><code> do subscribe
assertSubSucceeded
startRoundtripTimer
assertRecievedWebHook "channel_occupied"
stopRoundtripTimer
unsubscribe
assertRecievedWebHook "channel_vacated"
</code></pre>
<i></i><i>If you haven’t come across monads before, don’t worry, you can think of the monad we are using as “code that does IO”.</i><i></i><p>On top of this, once you have a basic intuition for how monads work, you start gaining the ability to describe what your program does at the type level. For example, This block of code requires a read-only environment (Reader), has logging output (Writer), and accesses websockets (WebSocketT).<p><i></i><i>Also, the fact that Haskell is a less mainstream language led to a few obvious disadvantages: less documentation, not quite as many libraries, poorer tooling (although having on the fly type errors show up with mod-ghc is already a massive win over Ruby).</i><i></i><p>Yes, you essentially need to be prepared to do any or all of the following if you want to use Haskell commercially:<p>1) Patch libraries and tooling.<p>2) Engage communities and maintainers (bug reports, mailing list, etc).<p>3) Implement functionality (from scratch) which "should be there".<p>If you can't, or won't, do these things, Haskell will be much more painful to use in the real world.
I know nothing about the Pusher protocol, but the standard approach to boilerplate encoding/decoding problems these days is to use GHC.Generics.<p>It doesn't always fit (typically when your data structures don't resemble the wire encoding), but it kills all the boilerplate when it does.<p>Since you say that you can magically get away without boilerplate in Ruby in this case, I would expect that the Generics approach will give exactly the same result.