Maybe promises will be the vehicle that makes understanding the usefulness of monads available for the masses.
Alternatively, promises may fail the acceptance test for the same reason as monads - because the average developer simply doesn't care / doesn't get it.
I really hope for the first scenario, though.
In Haskell, for type safety, you have to explicitly write this conversion and the function which does it has the somewhat-confusing name `return`. The `.then()` method then has the name `>>=`, pronounced "bind".
If you have these, you have a template for type-safe functional metaprogramming. Given any x, you can produce "a program which produces an x" (return), and given any function from an x to a program which produces a y, you can bind that onto a program which produces an x, to produce a program which produces a y:
return :: x -> ProgramWhichProduces x
(>>=) :: ProgramWhichProduces x -> (x -> ProgramWhichProduces y) -> ProgramWhichProduces y
This was huge historically because you can still metaprogram in a purely functional language, even though you cannot do things which have side-effects. So I can give you a pure primitive like `println :: String -> ProgramWhichProduces Nothing` and you can build `println("Hello, world!")` with it, and there are no side-effects in running that function on that input, so this is purely functional. In this way the act of actually running a `ProgramWhichProduces x` as an executable is deferred until the language is no longer in play: when the compiler is run, it looks for a `ProgramWhichProduces Nothing` called `main` and writes that out as the actual executable generated by the compiler.Monad refers then to precisely this design pattern of saying "I have some trivial embedding `x -> m x` into my problem domain `m` and I have some way to combine an `m x` with an `x -> m y` to produce an `m y`, hey, that's a monad!" ... One example is list comprehensions; there is a trivial way to embed any element as a singleton list [x] and then you can always use any function x -> [y] (called a "transducer" in Clojure) to process a list [x], concatenating together all of the [y]'s you get along the way. With ES6 generators this looks like:
function* forEach(iterator_x, ys_from_x) {
for (let x of iterator_x) {
yield* ys_from_x(x);
}
}
Hey, that's a monad. You can write a list comprehension like: // equivalent to Python's [f(x, y) for x of list_x for y of list_y if predicate(x, y)]
forEach(list_x, x => forEach(list_y, y => predicate(x,y) ? [f(x, y)] : []))
In Haskell you can write all monads with a special syntax called `do`-notation; you can write the above in any of three ways: [f x y | x <- list_x, y <- list_y, predicate x y]
-- which desugars to...
do
x <- list_x
y <- list_y
if predicate x y then [undefined] else []
return (f x y)
-- which desugars to almost exactly the JS above...
list_x >>= \x ->
list_y >>= \y ->
(if predicate x y then [undefined] else []) >>= \_ ->
return (f x y)
So that's the general pattern of monads and do-notation in a short, sweet lesson.Trying to drag monads into arbitrary languages like Javascript isn't some kind of best practice, it's cargo culting.
Monads are also not a "workaround for certain problems", contrary to what you hear sometimes, but an attempt to generalize a number of seemingly unrelated concepts into a uniform interface.
Uniform interfaces are good for programmers. This is the magic that monads bring to other languages as we get a handle of what the monad is for and what it does. You don't have to know what a monad is to make use of it. You don't need to know that Promises are a "continuation monad" to take advantage of the fact that async/await are simple tools to transform the "continuation monad" into something resembling synchronous code, which is a big win.
(Meanwhile Haskell points out that you can go simpler than async/await for your monad bindings if you like and use the same constructs for other monads too, but language developers are taking this one step at a time and trying to hit a happy medium where they have the power that monad binding gives but the discoverability and debuggability of more bespoke solutions to individual monads...)
Needless to say, it's not cargo culting so much as learning more about the very mathematical fundamentals of programming and using those fundamentals to build better languages with better features for better programs.
By describing them as monads and making the monadic pattern a core idea of the language, you can write algorithms that operate on any monad, so that you can write one function and have it be useful for both managing the execution of IO and error handling in a generic way.
People have an aversion to monads because a the concept of a monad has no grounds in reality, kind of like complex numbers. It is an abstraction that is useful not because it describes anything, but because it is simply a useful way to think.
I'm not sure how to answer this question, as my previous comment actually is the answer to your question.
Promises are a great example. They are a mechanism that provides better encapsulation and is still easier to compose than using pure callbacks.
(IIRC, Haskell made a similar transition: Monads haven't been in Haskell forever, many other ways have been tried before, but in the end turned out to be quite cumbersome and ugly in larger code bases. The introduction of monads was generally perceived as relieve in Haskell, and are becoming more and more popular in other non-lazy functional languages as well, such as OCaml.)
For one, only the IO monad could be said that is used in Haskell as a "workaround".
Second, monads are fundamental mathematical objects, not some random kludge invented to solve some language's problems.
Third, if you don't have monads in your language you still use them all the time, just implementing them yourself, in an ad-hoc and less flexible way.
You might as well ask what's the point of having Iterable<> when you're programming in C :)
It's not that you strictly _need_ monads or Iterable interfaces, it's that they represent an abstract concept which allows you to solve problems in a generic and type-safe way
I wouldn't probably "drag monads" into Javascript, but I'd surely appreciate Functors when programming in Javascript :)
No. Rather "monads capture the essence of certain problems".
Yes, it's an excedingly common misunderstanding and probably one of the main causes of people being "turned off" by promises. It's kind of frustrating to see so many blog posts making this mistake when critiquing them.
You can try and make 100% of your asynchronous methods return promises but what happens when you need to work with a library that can't use promises? Do you write a callback for that? Now you have a mixture and sure a few of these no big deal but when the code base balloons and you start having one offs all over the place now you need to figure out what returns what and code specifically to that.
Maybe if promises were in ECMAScript from the beginning then everyone would have used them and it wouldn't be a huge deal but right now it can get a bit frustrating, in my opinion. The stack traces of these nested promises are never fun to dive into as well though that likely depends on whether you're using a polyfill or not.
For example, imagine you have a charting library and you want to draw a chart, but you don't know if the data is available already or if it's being loaded asynchronously. You can just write:
// `drawChart(data)` is a synchronous function
// `data` may be the data itself or a promise that resolves to the data
Promise.resolve(data).then(drawChart)
For me, this is actually the killer feature of Promises, avoiding callback hell is just a nice side-effect. Once you start thinking of Promises as placeholders for values that may or may not be known already (rather than just an abstraction of the `callback(err, value)` pattern), you can write some really elegant code.[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Thanks for the info!
[1] http://www.krissiegel.com/rants/2015/11/7/settimeout-sucks