&gt; There is no stack overflow. A chaining of monads translate to a blocking of the continuation. The familiar imperative sequence of blocking operations is an instance of the continuation approach, and it isn&#x27;t susceptible to stack overflows.<p>So wait, you <i>are</i> doing TCO? When my code &quot;unblocks&quot; it will look like it&#x27;s continued directly from the previous line, when actually the previous line called into some other code which then called into a continuation for the &quot;tail&quot; of this method, right?<p>&gt; When you block a kernel thread, the OS takes care of all the continuation stuff -- but it does exactly that (the OS does run your single-threaded program in a continuation). Just as the OS mechanics of continuations don&#x27;t get in the way of your understanding of the above code, language-level implementation of continuations shouldn&#x27;t either. It is completely transparent to you -- your thread blocks, some handler does something, and then your thread resumes.<p>If it&#x27;s the kind of thing that can be implemented transparently then I don&#x27;t think I&#x27;d want to be managing it at all (and automatic CPS-ization is a valid alternative to threading (indeed as you say threading can be viewed as just a crude form of it) that can have performance advantages, but if it looks the same at the code level then it&#x27;s &quot;just&quot; a performance optimization). The point of the monadic style is for if I do want to manage the sequencing explicitly.<p>More concretely I think the big advantage is that a monadic effect can be captured as a value. So if I want to e.g. perform some effectful operation on a list, and I don&#x27;t know about traverse, I can just do the operation in the normal way with map, and then as long as I use those values in a way that makes sense according to the normal rules of the language then I&#x27;ll do something that makes sense. You draw an analogy with checked exceptions in Java - but most Java developers avoid using them, regard them as a failure, and I think a big part of that is you can&#x27;t safely factor out the result of a call to a possibly-exception-throwing method as a value.<p>&gt; Well, it&#x27;s sort of my made-up term that I use because &quot;functional&quot; doesn&#x27;t really have a definition (try to come up with a definition that encompasses all of: Lisp, ML, Haskell, and Erlang but none of Java, Ruby, Smalltalk, JS and you&#x27;ll see why), so I use the term to denote languages which we call functional (whatever exactly that means) which aren&#x27;t pure, so: the Lisps, MLs, Erlang.<p>Do you just mean non-Haskell languages?<p>I don&#x27;t think &quot;pure&quot; is binary in that sense: all languages have some things that are managed implicitly by the compiler&#x2F;runtime&#x2F;infrastructure and some things that the developer is expected to manage implicitly. Haskell explicitly sequences I&#x2F;O but the infrastructure manages laziness, whereas in most MLs it&#x27;s the other way around. Which is more pure is a matter of perspective.