9 ms·
Just to point out the probably obvious thing: that's only one type of monad (the state monad). There are many other things which form a monad and can't be repre
by pka 9y ago
Just to point out the probably obvious thing: that's only one type of monad (the state monad). There are many other things which form a monad and can't be represented easily in JS or other imperative languages.
Even though even the state monad is a clear win in my opinion (since it explicitly delimits a stateful computation which can then be embedded in a pure one in a principled way), the concept itself is much broader and thus much more useful than just allowing for imperative syntax in a pure language. For example, the continuation monad, which is just a library in Haskell, can't be implemented in most imperative languages without extending the language itself (async in JS, C#, etc).
- jerf 9y agoTo emphasize, of course the monad interface isn't useful if the only implementation of it that you use is also the monadic implementation used by an imperative language. But if you "just" use an imperative language, you lose all the other implementations because the language hardwires the "conventional" one at the bottom. You lose the parser implementation of the monad interface, the free monad implementations (which are useful for writing "programs" that are then separated from their interpreter), the software transactional memory implementation, the probability implementation, the effects-controlling implementations (that go beyond "either in IO or not" to implement more granular controls), and the many other things that have been implemented. Obviously that's ultimately a viable solution since almost every programming language works that way. But if you're going to ask "is the ability to implement the monad interface worth it for some given language?", you aren't going to get anywhere if you consider only one of the many implementations of the interface. Why would anybody use the iterable interface if all it could do is the equivalent of "for (i = 0; i < arr.len; i++)" and couldn't iterate over a binary tree or hash table as well? Why would anyone use any interface if an interface only has one implementation, ever? The problem lies in the question, not the value of the interface. (I'm trying to get better at phrasing my posts about "monads" better because one of the major problems they have is that people think of "monad" as a noun. But it is an interface, just like in Java or a Rust trait, that can be implemented by a type; it just so happens that the interface is not something that can be cleanly represented in most statically typed languages because of the high-order polymorphism. "Monad" is an adjective, not a noun. And "monads" aren't "about managing state" any more than the Iterable interface is about "incrementing an integer to walk over an array"; the conceptual error is the exact same kind in both cases.)
- skybrian 9y agoThis is a bit like saying "writing your own embedded mini-language and interpreter is better since you can make it do arbitrary things". The question is whether you want people on a large project spending time on custom tools? There's benefit in simplicity and standardization. Using Rust (to pick a nicer example than JavaScript) and the community's standard language tools does have its advantages. I say this as someone who likes to work on language tools. It's fun, but I wouldn't call it productive, unless you work on tools with a large userbase.
- eastWestMath 9y agoThat's literally the opposite of what he's saying - the entire point of monads are their uniform interface for different abstractions.
- mark_l_watson 9y agoA little off topic, but I wanted to thank you for your comment ‘..delimits a stateful computation which can then be embedded in a pure one in a principle way’ I always think of it the other way around: I write pure code, and then embed the pure code in IO, etc. I tend to have boilerplate bits of impure code that I use as an after thought once I have pure code written and tested in a repl. I think that to progress, I need to swap my view of Haskell, and spend much more time on the impure parts, and earlier in my dev process.
- gowld 9y agoThen you look at professional code written by the famous experts, and you see almost everything is written inside an app-specific ReadWriteStateIO monad.
- lmm 9y agoMost apps do things. But most apps don't do everything. Of course most business code is written in domain-appropriate monads that express some set of effects; why would we expect anything different?
- lojack 9y ago> For example, the continuation monad, which is just a library in Haskell, can't be implemented in most imperative languages without extending the language itself (async in JS, C#, etc). I don't know about other languages, but the continuation monad is pretty trivial to write using promises. These can either be provided natively or using a promise library. It's a little more verbose than Haskell, but it's actually a useful pattern in JS. https://codepen.io/anon/pen/JpLLJR?editors=0011 https://codepen.io/anon/pen/JpLLJR?editors=0011 FWIW, I'm no JS expert, so its probably likely that there's a library out there that provide the functionality of applyAsync and composeAsync that I wrote.
- pka 9y agoOf course! When I said "it can't be implemented" I meant the logic that the continuation monad itself encapsulates, not async computations in general. For those you wouldn't even need promises, you could just use callbacks: fetchUser("url", (user) => { fetchAddress(user.address, (address) => { ... }); }); But this repetetive plumbing is exactly what the continuation monad abstracts away: do user <- fetchUser "url" address <- fetchAddress user.address ... So the point is that you can write your async code in the same way you'd write normal, sequential code (which is what the async extensions of JS/C# allow you to do).
- rbehrends 9y agoThis is a syntax thing only. You can do the same in Smalltalk with an array of blocks (which are closures). { [ user := url fetchUser ]. [ address := user fetchAddress ]. } and pass that array as an argument to whatever evaluation strategy you want. You can also do with with proper macros, e.g. in Nim (fictitious example, there's no actual `usingContinuations` macro): usingContinuations do: user = fetchUser("url") address = user.fetchAddress This is all a matter of syntax allowing you to write a sequence of code fragments in a readable fashion while allowing transformations on the underlying sequence. That said, outside of Haskell (or other functional languages emulating the approach) you'll encounter them fairly rarely in this particular form, because just allowing for a sequence of code fragments is a bit limiting if you can combine them in more general ways. For example, Smalltalk builds pretty much all control flow in what we'd call combinators (on closures and values) now and allows you to pretty much extend that arbitrarily. Not because Smalltalk does anything super-special here, but because it has a nice, concise syntax for expressing executable code fragments as values.
- nnq 9y ago> delimits a stateful computation which can then be embedded in a pure one in a principled way You probably meant to say smth different, but to many this reads like "you can embed and call impure code inside pure code while the pure code stay pure" which doesn't make much sense. I mean, you always do the opposite: embed/call pure code from the impure one.
- efnx 9y agoWith the state monad (State not StateT) you can run it in pure code because the runner is pure. It only “mutates” one context and when it’s all unwrapped it’s actually pure functions all the way down.
- tome 9y agoYou probably meant ST, but yes, much to nnq's surprise, this is possible!
- gowld 9y agoThe fact that something called "ST" is one of the most important libraries in the language, and it's the one that you should choose for State (leave aside the "State" library! that's not the one you want!) shows how for all Haskell's power, it's terribly unusable for trivial reasons that never get fixed due to extreme deference to legacy mistakes. See also: Prelude.
- tome 9y agoIt's not one of the most importart libraries in the language. It's hardly used.
- tathougies 9y agoST is not the monad that should be used for state. You should use State for that. ST is an optimization trick mainly used by framework implementors. In my professional experience writing Haskell, I’ve never seen st used for anything important
- ernst_klim 9y agoIndeed, one of the advantages monads give is the abstraction over different types of computations with side effects (state, exceptions, non-determinism etc) Great tutorial: http://binaryanalysisplatform.github.io/bap/api/v1.3.0/Monads.Std.html http://binaryanalysisplatform.github.io/bap/api/v1.3.0/Monad...