5 ms·
I think the killer app of the monad is really the language feature that Haskell has called "do notation". This is where it really shines. You can definitely use
by bweitzman 8y ago
I think the killer app of the monad is really the language feature that Haskell has called "do notation". This is where it really shines. You can definitely use the monad interface in other languages, but it can feel clunky and unintuitive sometimes because you end up either foregoing some of the effectiveness to better fit the language, or you end up in essentially callback hell. Example:
In Haskell, you can use do notation:
do
x <- someFunction
y <- anotherFunction x
z <- yetOneMoreFunction y x
return (x + y + z)
This is syntactic sugar for something like (using `flatMap` in place of >>= (bind) ):
flatMap someFunction (\x ->
anotherFunction x (\y ->
yetOneMoreFunction y x -> (\z ->
return (x + y + z)
)
)
)
If Haskell programmers had to write code like this (which is what monads look like in other languages), there's no way they would use monads!
In languages with objects, you can sometimes get away a minor victory by using what I'll call a "shallow bind". Consider promises in javascript (which form a monad with unit a = Promise.resolve(a) and flatMap p f = p.then(f) ):
someHttpRequest().then( response => {
if (response.body == undefined) {
return Promise.reject("no body error");
} else {
return Promise.resolve(response.body);
}
}).then(body => {
try {
return Promise.resolve(JSON.parse(body));
} catch (e) {
return Promise.reject(e);
}
}).then(parsed => {
if (parsed.status == undefined) {
return Promise.reject("no status");
} else if (parsed.status == 500) {
return Promise.reject("server failure");
} else {
return Promise.resolve(data);
}
// what would you do if you decided you wanted to reference `response` here somewhere?
});
This is definitely an improvement over a deeply nested callback style from 10 years ago, but it does sacrifice some flexibility. You can't refer to previously handled promises because the callbacks are shallow. You can do that with do-notation, and that's what makes the concept of monads stick in Haskell.
Once you have that, and in Haskell, it's amazingly implemented as an overloadable piece of syntax, the doors are wide open. Example: at my work, we use free monads to get a similar effect as dependency injection in other languages (clean separation of concerns, easy mocking, etc) but with two huge benefits: 1. Any function is limited to using only the dependencies/effects you allow it to, it won't type check if you try to sneak something in, and 2. Because of the overloadable syntax, we use it to collect timing info on all of our operations that are doing IO in a way where we get a flame graph style hierarchy of effects _for free_. We never have to specify when to start and a stop the timers, the syntax knows when to. It's pretty powerful.
- chadcmulligan 8y agooh ok, thats a great example - many times I've had to kludge my way through doing just that with promises. thats very exciting :-)
- KirinDave 8y agoAside, don't you find the performance costs of Free too great? Even with Codensity optimization, I hit painful performance limitations on some work.