Monad tutorial #20,067 that still doesn't just show the code to implement a real-world-applicable monad and then explain it.
And part 2 of this article, which might have been that, has been deleted.
So I'm still convinced that monads aren't real, and this is all a prank to get non-Haskell developers to read nonsensical doublespeak until they get bored and go back to work.
Hi, author here. Thanks for the feedback! What did you have in mind specifically as a real-world-applicable monad? I can PM you with a secret link to the deleted post (currently revising it.)
One example I've wound up using recently is the one found in Pike's Errors are values where he pretty much implements an ad-hoc monad, but without any of the explicitness and generality that monad-aware languages do. As, in, the code he ends with,
b := bufio.NewWriter(fd)
b.Write(p0[a:b])
b.Write(p1[c:d])
b.Write(p2[e:f])
// and so on
if b.Flush() != nil {
return b.Flush()
}
should be roughly equivalent to the following pseudo-Haskell
runWriter fd $ \b -> do
write b p0[a:b]
write b p1[c:d]
write b p2[e:f]
flush b
or in pseudo-Rust with a stabilised try:
let b = bufio::new_writer(fd);
try {
b.write(p0[a:b])?;
b.write(p1[c:d])?;
b.write(p2[e:f])?;
return b.flush()?;
}
As in, Haskell and Rust would be more explicit about the failure scope, and both the do block and the try and ? signal to the user that here are fallible operations that might not happen, unlike in Go where b might just silently become inert. I guess for this example the "look samey" impulse was stronger than the "be explicit" impulse?
In any case, the Go example should serve to kick off a discussion of an Either/Result monad¹ with a reference to a "real-world" language that is normally the opposite of interested in monads.
¹ Yes, I called the pseudo-Haskell function "runWriter" but that's just because I felt sticking with the "newWriter" name was even wronger
Refactoring Ruby with Monads has always been my favourite “why should every-day developers give a shit” article. No theory, just showing that three types actually naturally end up having a similar, and very useful, interface, and that interface is exactly what monads are.
Java does have monads, they just reinvented them badly. Stream and Optional have both map and flatMap. CompletableFuture uses the name thenCompose instead of flatMap. The name is not terrible, but they missed the opportunity to create a standard monadic API, because Java...
They also did some really annoying things with their support for the Optional monad, their andThen/flatMap function will always return None of passed in a null value, meaning that you lose information: None /= Some(null).
I can understand why they did it, but it violates the monadic laws. The Scala solution was to have the Option constructor convert null to None, but you can still create Some(null) if you want.
Java maintainers also did not want Optional to be used as values in classes, it was intended only for return values, which is nuts.
They didn't miss it, the language's type system is simply not expressive enough to create a proper Monad abstraction - which is a tradeoff.
Like, it's pretty damn dumb on your part to assume that the Java designer team, who are possibly one of the most experienced language designers wouldn't know about "Functional Programming 101" type of knowledge..
Generics were added to Java by Haskellers who, I’m sure, would have loved to give Java a truly powerful type system, but I’d guess they (or Sun?) thought it was too ambitious.
There's no point in having a monadic API if you can't abstract over it. There are no HKTs in Java, therefore there's no need to have a unique interface.
login :: AuthMonad m => User -> Credential -> m UserAuth
which allows login to be used in any monad which has an instance for AuthMonad, so you can change the effects your program uses without changing the business logic.
I've used Scala for 10+ years and never felt the need for the reader monad. Options are fine, I don't know why you lump them in the terrible category. You can of course use for-comprehensions with Option, but mainly I use getOrElse, or helper conversion functions like IO.fromOption.
Java Futures certainly are terrible, but Futures are fine in Scala, again thanks to for-comprehensions. I haven't used async/await, but AFAIK it is no better than using an IO monad with for comprehensions.
Right, Scala does not need the Reader monad because it gets around that with syntax sugar: implicit paramters.
Yes, Futures are fine in Scala because of language sugar and HKTs: HKTs enable for comprehensions. Try to use Futures without for comprehensions.
The reason I'm hating on Option is because Monad composability sucks. Once you have more than one, it becomes a giant mess of unreadable code. Since nullability is very common, you run into this way sooner, e.g. when combining Options and Lists or Options, Lists and Futures.
Take a look at Kotlin or heck, even modern JavaScript. It's super easy to use and much more readable, e.g. compare the following:
Well, this is just the usual case of specialization vs generalization.
I do think that specialization is better to use, but not having it generalized means a shitton of duplicated code, e.g. think of writing something like a thread pool that would work with both Kotlin coroutines and some other coroutine-like library's primitives.
Having dedicated syntax is a reflection of the fact that monads are so useful, and general. You going to start complaining about OOP languages using dot application of methods instead of just passing objects as the first argument to methods too? What about the garbage that is the various loop syntaxes? After all, they encourage uncomposable code that can always be written from simpler parts (with the same performance).
Do you really want to be writing andThen … andThen … andThen all the time? In languages which don’t have dedicated syntax, you don’t have a choice, and it massively obscures the readability of code.
There's no need for standard monadic API in Java or any language with a similar type system, as they cannot express higher-kinded types, and therefore cannot generalize over different types of monad.
The world needs to know about monads so Go developers can stop using the most fucking braindead, repetitive, noisy and intrusive error handling known to man.
Hi, author here. I totally agree, there have been some other threads on HN that have also brought it up and the distinction is important. I will make that the revision to part 1 includes this.
Monad is a simple pattern, and most languages are reinventing them badly. Java has Optional, Stream and CompletableFuture. Javascript has Promises (and I guess Observables and React are also monad-like).
bruv if you don't want to interact with programmers about monads, don't interact with a /r/programming post on monads. the power was inside of you all along
It's unfortunate that the collective mind has been poisoned. We could've had basic and useful things like sum types, result, closures, higher-order functions, immutability as a feature and so on in the '90s instead of the late '10s.
Before working on Scala, Martin Odersky created pizza. Java could have had those features in the early 2000s, but the maintainers were only interested in generics.
Monads were still pretty cutting edge in the 90s. Today, every language should support at least basic monads, but the OO community has to get over their fear of simple FP concepts.
I mean, Java does have algebraic data types, generics, lambdas, and pattern matching now. There was just a slowing down of development at the end of Sun, that has fortunately changed for the better with Oracle (surprisingly).
Naw, the real reason is objects are pretty straightforward to add to C, see C++ (1985). So OOP got to bootstrap off of C where FP was off doing its own thing. Which is somewhat necessary because C doesn't lend itself to any of those features without some major overhauls.
Monads are like good managers in corporate, they make sure nobody disturbs their team, instead offering well established way for providing instructions for them.
Immediately we know that there are also bad managers, that there are some criteria to distinguish between the two, that manager job varies a loooot between each position, truly universal part is just those established ways of providing instructions so that the team can do whatever they do with sufficient quality.
I just think of foreach as pretty much the standard for as opposed to the C-style for. In Haskell it's just map with the arguments swapped iirc.
You can also do a for foo in bar { … } in Rust, but the compiler will suggest that you rewrite it as an if let Some(foo) = bar { … }:
warning: for loop over an `Option`. This is more readably written as an `if let` statement
--> src/main.rs:3:16
|
3 | for foo in bar {
| ^^^
|
= note: `#[warn(for_loops_over_fallibles)]` on by default
help: to check pattern in a loop use `while let`
|
3 - for foo in bar {
3 + while let Some(foo) = bar {
|
help: consider using `if let` to clear intent
|
3 - for foo in bar {
3 + if let Some(foo) = bar {
|
Scala std lib does have tap and pipe for collections. There are proposals to add tap to Option. I never occured to me that tap is equivalent to foreach for Option...
For a Spanish speaker, "monad" sounds very close to "monada", which means "cutie". I strongly believe your life as a programmer will be happier if you choose to assign that meaning to the word.
I agree with you, but bellow I gotta be a huge nerd and explain technicalities. If you don't like that stuff, ignore me.
There's a difference between mónada and monada. The first one is the one used to translate monad as in category theory or the Leibnizian concept. The second is a cutie, or a silly (monkey like) behavior.
Yet explanations typically swing between high-level metaphors and deep mathematical abstractions. Each approach offers part of the picture, intuition without precision, or rigor without intuition but seldom both.
All the other tutorials do it wrong, but this time the author will get it just right!
Jokes aside I thought it was pretty well written. I haven't thought about C# Tasks as a monad before, so I'm looking forward to part 3
Hi, author here. That's a great point! I didn't realize that my post would garner so much attention, but I will address your feedback in the revision for part 1.
Perhaps you go into this already in part 2, but explaining how to enable LINQ syntax for your own Monad type could be a fun little side article. LINQ is like do-notation within C# and not many people know how to add LINQ support for a new types.
Well thats because the pattern is pretty generic. Its basically "wrap up some common control flow pattern with a nice interface". List: for loop, maybe: null check, either: exception handling
The nice part about c# is that it's flexible enough to take pieces that you like from various paradigms without needing to fully commit. I like lists, maybes and eithers, but don't feel like using the Reader stuff.
AdvancedSandwiches@reddit
Monad tutorial #20,067 that still doesn't just show the code to implement a real-world-applicable monad and then explain it.
And part 2 of this article, which might have been that, has been deleted.
So I'm still convinced that monads aren't real, and this is all a prank to get non-Haskell developers to read nonsensical doublespeak until they get bored and go back to work.
recover__password@reddit
Hi, author here. Thanks for the feedback! What did you have in mind specifically as a real-world-applicable monad? I can PM you with a secret link to the deleted post (currently revising it.)
AdvancedSandwiches@reddit
Wait, do you not know what the real whirls application is?
syklemil@reddit
One example I've wound up using recently is the one found in Pike's Errors are values where he pretty much implements an ad-hoc monad, but without any of the explicitness and generality that monad-aware languages do. As, in, the code he ends with,
should be roughly equivalent to the following pseudo-Haskell
or in pseudo-Rust with a stabilised
try
:As in, Haskell and Rust would be more explicit about the failure scope, and both the
do
block and thetry
and?
signal to the user that here are fallible operations that might not happen, unlike in Go whereb
might just silently become inert. I guess for this example the "look samey" impulse was stronger than the "be explicit" impulse?In any case, the Go example should serve to kick off a discussion of an
Either
/Result
monad¹ with a reference to a "real-world" language that is normally the opposite of interested in monads.¹ Yes, I called the pseudo-Haskell function "runWriter" but that's just because I felt sticking with the "newWriter" name was even wronger
Axman6@reddit
Refactoring Ruby with Monads has always been my favourite “why should every-day developers give a shit” article. No theory, just showing that three types actually naturally end up having a similar, and very useful, interface, and that interface is exactly what monads are.
https://tomstu.art/refactoring-ruby-with-monads
piesou@reddit
Imagine Java naming their Iterator interface Isonumeronator and all the blog articles it would spawn.
KagakuNinja@reddit
Java does have monads, they just reinvented them badly. Stream and Optional have both
map
andflatMap
. CompletableFuture uses the namethenCompose
instead offlatMap
. The name is not terrible, but they missed the opportunity to create a standard monadic API, because Java...Axman6@reddit
They also did some really annoying things with their support for the Optional monad, their andThen/flatMap function will always return None of passed in a null value, meaning that you lose information: None /= Some(null).
KagakuNinja@reddit
I can understand why they did it, but it violates the monadic laws. The Scala solution was to have the Option constructor convert null to None, but you can still create Some(null) if you want.
Java maintainers also did not want Optional to be used as values in classes, it was intended only for return values, which is nuts.
Axman6@reddit
“What would those damn academics know, they just think about programs, instead of hacking shit together like us Real Developers™”
Ok-Scheme-913@reddit
They didn't miss it, the language's type system is simply not expressive enough to create a proper Monad abstraction - which is a tradeoff.
Like, it's pretty damn dumb on your part to assume that the Java designer team, who are possibly one of the most experienced language designers wouldn't know about "Functional Programming 101" type of knowledge..
Axman6@reddit
Generics were added to Java by Haskellers who, I’m sure, would have loved to give Java a truly powerful type system, but I’d guess they (or Sun?) thought it was too ambitious.
piesou@reddit
There's no point in having a monadic API if you can't abstract over it. There are no HKTs in Java, therefore there's no need to have a unique interface.
KagakuNinja@reddit
As a Scala programmer, I rarely abstract over the type of monad. I use libraries that certainly do.
Having a consistent monadic interface in the Scala standard library for all collections, Option, Try, Either and Future is extremely useful.
By contrast, in Java there are multiple inconsistent interfaces for monad-like classes, which means more junk to memorize.
Axman6@reddit
It’s very common in Haskell to say things like:
which allows
login
to be used in any monad which has an instance for AuthMonad, so you can change the effects your program uses without changing the business logic.piesou@reddit
Right, Scala has do notation as well which makes it convenient to use (which also builds on the Interface being present).
KagakuNinja@reddit
I've used Scala for 10+ years and never felt the need for the reader monad. Options are fine, I don't know why you lump them in the terrible category. You can of course use for-comprehensions with Option, but mainly I use getOrElse, or helper conversion functions like IO.fromOption.
Java Futures certainly are terrible, but Futures are fine in Scala, again thanks to for-comprehensions. I haven't used async/await, but AFAIK it is no better than using an IO monad with for comprehensions.
piesou@reddit
Right, Scala does not need the Reader monad because it gets around that with syntax sugar: implicit paramters.
Yes, Futures are fine in Scala because of language sugar and HKTs: HKTs enable for comprehensions. Try to use Futures without for comprehensions.
The reason I'm hating on Option is because Monad composability sucks. Once you have more than one, it becomes a giant mess of unreadable code. Since nullability is very common, you run into this way sooner, e.g. when combining Options and Lists or Options, Lists and Futures.
Take a look at Kotlin or heck, even modern JavaScript. It's super easy to use and much more readable, e.g. compare the following:
Ok-Scheme-913@reddit
Well, this is just the usual case of specialization vs generalization.
I do think that specialization is better to use, but not having it generalized means a shitton of duplicated code, e.g. think of writing something like a thread pool that would work with both Kotlin coroutines and some other coroutine-like library's primitives.
Axman6@reddit
Having dedicated syntax is a reflection of the fact that monads are so useful, and general. You going to start complaining about OOP languages using dot application of methods instead of just passing objects as the first argument to methods too? What about the garbage that is the various loop syntaxes? After all, they encourage uncomposable code that can always be written from simpler parts (with the same performance).
Do you really want to be writing andThen … andThen … andThen all the time? In languages which don’t have dedicated syntax, you don’t have a choice, and it massively obscures the readability of code.
neopointer@reddit
Maybe they didn't miss the opportunity, but rather dodged a bullet?
vytah@reddit
There's no need for standard monadic API in Java or any language with a similar type system, as they cannot express higher-kinded types, and therefore cannot generalize over different types of monad.
ketralnis@reddit (OP)
Imagine programmers thinking they invented numbers and refusing to acknowledge arithmetic
pm_plz_im_lonely@reddit
Gauss in shambles.
Axman6@reddit
The world needs to know about monads so Go developers can stop using the most fucking braindead, repetitive, noisy and intrusive error handling known to man.
ebingdom@reddit
A list is not a monad. List is a monad.
looksLikeImOnTop@reddit
As someone who only vaguely understands monads this is a very helpful distinction
Axman6@reddit
This might help you understand them better, no theory, just “hey, look, these things programmers use everyday all have the same interface”
https://tomstu.art/refactoring-ruby-with-monads
recover__password@reddit
Hi, author here. I totally agree, there have been some other threads on HN that have also brought it up and the distinction is important. I will make that the revision to part 1 includes this.
yawaramin@reddit
Please, no more monads 🙅♂️
Maybe-monad@reddit
Wish not granted
Axman6@reddit
Nothing to see here.
KagakuNinja@reddit
Monad is a simple pattern, and most languages are reinventing them badly. Java has Optional, Stream and CompletableFuture. Javascript has Promises (and I guess Observables and React are also monad-like).
yawaramin@reddit
When I said 'Please, no more monads' I didn't mean 'Please tell me more about monads'.
Talking about 'monads' has turned out to be a huge waste of time for programmers in general. Just use monadic binding, enjoy, and give it a rest 🙏
EldritchSundae@reddit
bruv if you don't want to interact with programmers about monads, don't interact with a /r/programming post on monads. the power was inside of you all along
TankAway7756@reddit
It's unfortunate that the collective mind has been poisoned. We could've had basic and useful things like sum types, result, closures, higher-order functions, immutability as a feature and so on in the '90s instead of the late '10s.
KagakuNinja@reddit
Before working on Scala, Martin Odersky created pizza. Java could have had those features in the early 2000s, but the maintainers were only interested in generics.
Monads were still pretty cutting edge in the 90s. Today, every language should support at least basic monads, but the OO community has to get over their fear of simple FP concepts.
syklemil@reddit
They did get onboard with lambdas at some point, at the very least. Even Java has them now, after deriding them as FP nonsense!
KagakuNinja@reddit
Apparently they told Odersky that Java did not need lambdas, since Java anonymous inner classes were equivalent to closures.
Ok-Scheme-913@reddit
I mean, Java does have algebraic data types, generics, lambdas, and pattern matching now. There was just a slowing down of development at the end of Sun, that has fortunately changed for the better with Oracle (surprisingly).
mot_hmry@reddit
SML was 1983... 😭 Miranda 1985.
I blame Lisp, lol jk.
Naw, the real reason is objects are pretty straightforward to add to C, see C++ (1985). So OOP got to bootstrap off of C where FP was off doing its own thing. Which is somewhat necessary because C doesn't lend itself to any of those features without some major overhauls.
gareththegeek@reddit
You're a monad
neopointer@reddit
You're a monad, you're a monad and YOU'RE A MONAD!!
EVERY F*** BODY IS A MONAD!!!!11!!!ONE!!
Shivalicious@reddit
Thanks. You made me laugh so hard I couldn’t concentrate on anything.
lunchmeat317@reddit
Your mom's a monad
Difficult_Loss657@reddit
https://i.imgflip.com/9z6xaf.jpg
KagakuNinja@reddit
Do you even lift, bro?
Maybe-monad@reddit
Yup
gareththegeek@reddit
Name checks out
neopointer@reddit
I wish I had this much time.
bonerfleximus@reddit
Well ya, according to Gottfried Liebnitz the Monad was the atomic building block of reality.
przemo_li@reddit
Monads are like good managers in corporate, they make sure nobody disturbs their team, instead offering well established way for providing instructions for them.
Immediately we know that there are also bad managers, that there are some criteria to distinguish between the two, that manager job varies a loooot between each position, truly universal part is just those established ways of providing instructions so that the team can do whatever they do with sufficient quality.
930913@reddit
A
Maybe
monad is just aList
wherelength <= 1
.Maybe-monad@reddit
Call me List again?
grrangry@reddit
It's an array!
Maybe-monad@reddit
You weren't supposed to see that!
YeetCompleet@reddit
In Scala their "Maybe" (Option) even has
foreach
syklemil@reddit
I just think of
foreach
as pretty much the standardfor
as opposed to the C-stylefor
. In Haskell it's justmap
with the arguments swapped iirc.You can also do a
for foo in bar { … }
in Rust, but the compiler will suggest that you rewrite it as anif let Some(foo) = bar { … }
:KagakuNinja@reddit
Scala std lib does have
tap
andpipe
for collections. There are proposals to addtap
to Option. I never occured to me thattap
is equivalent toforeach
for Option...jdehesa@reddit
For a Spanish speaker, "monad" sounds very close to "monada", which means "cutie". I strongly believe your life as a programmer will be happier if you choose to assign that meaning to the word.
Ok-Scheme-913@reddit
That explains the huge number of femboys among functional programmers!! Check mate.. someone I guess?
Linguaphonia@reddit
I agree with you, but bellow I gotta be a huge nerd and explain technicalities. If you don't like that stuff, ignore me.
There's a difference between mónada and monada. The first one is the one used to translate monad as in category theory or the Leibnizian concept. The second is a cutie, or a silly (monkey like) behavior.
valcron1000@reddit
YAMT ("Yet another monad tutorial")
T_D_K@reddit
I love the first paragraph
All the other tutorials do it wrong, but this time the author will get it just right!
Jokes aside I thought it was pretty well written. I haven't thought about C#
Task
s as a monad before, so I'm looking forward to part 3recover__password@reddit
Hi, author here. That's a great point! I didn't realize that my post would garner so much attention, but I will address your feedback in the revision for part 1.
SulszBachFramed@reddit
Perhaps you go into this already in part 2, but explaining how to enable LINQ syntax for your own Monad type could be a fun little side article. LINQ is like do-notation within C# and not many people know how to add LINQ support for a new types.
recover__password@reddit
Yeah I could go into how to use the linq query syntax in part 2 (or a future part)
Ythio@reddit
At this point people will take any generic class from .NET and call it monadic
T_D_K@reddit
Well thats because the pattern is pretty generic. Its basically "wrap up some common control flow pattern with a nice interface". List: for loop, maybe: null check, either: exception handling
The nice part about c# is that it's flexible enough to take pieces that you like from various paradigms without needing to fully commit. I like lists, maybes and eithers, but don't feel like using the Reader stuff.
Due_Practice552@reddit
thank you for giving good information😀
anonymous-red-it@reddit
Monads are just OOP
Weak-Doughnut5502@reddit
How do you figure?
jboges@reddit
Ihov
MrLyttleG@reddit
Je préfère la limonade
TemporalChill@reddit
The friends we made along the way...
Those are the real monads.