So let's talk about monads.

Discussion

When looking into monads, you will either find obtuse theory, Haskell code, or nonsense analogies(i think i'll have a burrito for lunch tomorrow). These explanations all have the same problem: Intentional or not, they presume the reader basically already knows what a monad is. That's great for people who've been initiated into functional programming and need a refresher, but does little for anyone else. Someone from an imperative background is going to look at this stuff, see more stuff they don't have the context to understand, and walk away frustrated. This is strange because every imperative programmer can easily understand the option/maybe types that have been imported into imperative languages. Of course they can! There are barely any moving parts.

Thus if you want to explain monads to an imperative programmer, you need to implement monads in an imperative language, and you need to talk about them in imperative terms. The concepts aren't difficult, but the language barrier is enormous. Maybe calling monads wrappers isn't 100% technically accurate for reasons that matter a lot to you, but I guarantee those reasons won't matter spit to someone if you can't get them over that initial hurdle. When explaining square roots to a child, you elide imaginary numbers until the child is comfortable with the basic idea.

If you want your ideas to be heard by others, then you must swallow your pride and adapt to the audience you want to reach. If you won't speak Imperative, then why should we learn to speak Functional? You cannot bridge the gap with arrogance.

I'm sorry if I've offended anyone, but I've been around this mess for years, and it isn't getting any better. If you're offended, then maybe you're the person who most needs to hear what I have to say.

Posted Jul 14, 2021, 6:13 AM UTC

1591
tesch34•

Is this a subreddit or a selfhelp group?

26
PL_DesignOP•

I consider this an intervention.

9†
raiph•

Fwiw, I consider it a successful one, despite the votes, and thank you for it. :)

10
hou32hou•

Monad is just an interface that happens to be useful in many seemingly unrelated area.

It’s just an interface, similar to ICollection in Java or Iterator trait in Rust.

From a programmer point of view.

23
curtisf•

An important point that is often lost is that a Monad is not an interface in the Java/C#/TypeScript/etc. sense.

A Monad is not a type but a family of types. If Java allowed type parameters to take type parameters, Monad could be reified as a Java interface. However, it doesn't, so it can't. Given that Java generics are totally erased, I'm not sure if there are really any deep reasons why type-parameters-with-type-parameters (higher kinded types) couldn't be added to Java.

The lack of "higher kinded types" is not really related to monads; they have uses way beyond monads. But if you don't have them in your type system, it's very hard to talk about what a Monad really is.

I suspect that a lot of confusion about "monads" is really confusion about higher kinded types. Monads are really just defined by flatMap, which is an uncommon but not super hard to understand function. Meanwhile higher-kinded types are abstract, and it's not obvious why you would ever "need" them, or even that Java "cannot" express them.

10
PL_DesignOP•

Is this so important? You don't need a type system to manually build concrete implementations, especially if you only care about demonstrating how a handful of monads behave.

3
epicwisdom•

Well, I think it is certainly a relevant distinction that a language like Haskell can have class Monad m whereas Java cannot have interface Monad<M>. Concrete examples are important, but the whole point of seeing the concrete examples is to identify the pattern and abstract it into the interface.

5
hou32hou•

Basically interface with higher kinded types

1
wsppan•
19
Nilstrieb•

This is perfect

2
crmills_2000•

This is the best explanation of functor and monad I have every seen. Fantastic!

2

"I'm mad about not understanding a monad pattern, and you can too!"

14
PL_DesignOP•

Correct!

1
omega1612•

I have think it like this all this time.

I'm a pure mathematician, but it was some kind of hard.

I mean, as mathematician I have done plenty of times the "In math you don't understand things. You just get used to them". So it was just easy to don't ask what a monad is. The real problem begins to me when I come the "how do you use this thing" part.

Even with all the explications, examples and more, i just end implementing State monad in python three times before I finally really understand that State monad+ Reader monad is a way to do a pure access to variables that regularly you do by impure global variables. Even various examples mentioned it but I didn't understand since they didn't put a it in that terms.

So I became to think: What if I do a "let's use python as pure language and use it to explain why FP works like it does" blog entry.

I did some examples and then I began to understand even better how FP works by myself XD

So I never did the blog entry, but maybe some day I could make it and add it to the "how monads are like burritos" list of tutorials XD

11
lambda-male•

My position is that you shouldn't ever teach monads or mention the word "monad" to anyone who isn't trying to learn (or already understands) Haskell-style purely functional programming (which can be done in impure languages, too). And even if they are, it should be mentioned only after they grasp types, parametric polymorphism, higher-order functions etc.

Sometimes other programmers will also encounter monad or monad-like instances (like a type for building cooperatively concurrent computations or the option/result type), but usually they will work with one of them at a time, and in this situation there is no point in mentioning monads.

you need to implement monads in an imperative language, and you need to talk about them in imperative terms.

Monads are purely functional (some use impure stuff behind the scenes, usually as an optimization), it wouldn't make sense to add imperative features such as loops/structured control flow or mutation to any explanation of monads. So you are talking just about syntax (in practice adding a few returns, commas, and various kinds of brackets :)?

9
PL_DesignOP•

I explained this poorly: What is necessary is to show several concrete examples of how the data is moving around, and then to extract the monadic interface from those. The more familiar the syntax and language conventions are to your listener, the better.

2
curtisf•

When you look up monads, you find Haskell code because Haskell is one of the rare languages with higher-kinded types. A Monad is a family of types, and so expressing it usually requires higher-kinded types. You cannot reify Monad as a construct into Java or C# or TypeScript or Go.

Monad isn't anti-"imperative" any more than Comparable or Iterable is. The reason you cannot implement a Monad in Java is not that Java is imperative/not-functional, but that Java lacks higher kinded types. Ditto C#, TypeScript, Go.

You can implement monads in these languages (e.g., Option<T>, List<T>, ...) but you cannot reify the claim that they are monads, nor can you write code that acts generically on an unknown monad. This makes it very hard to communicate why this "pattern" of defining generic types with a flatMap is "interesting" or "important" without using Haskell.

As has been mentioned by other commenters, you could define Monad in Java with four lines of code (Though I would use less opaque names like box and flatMap), if only Java were to support higher-kinded types. But it doesn't, which is why almost all explanations use a "obtuse" language like Haskell or Scala.

9
friedbrice•

Asking unironically: Do you speak Java? Does this help at all?

interface Monad<M> {
    <A> M<A> pure(A a);
    <A, B> M<B> bind(M<A> ma, Function<A, M<B> f);
}

static <A> M<A> join(Monad<M> m, M<M<A>> mma) {
    return m.bind(mma, ma -> ma);
}

static <A, B> M<B> fmap(Monad<M> m, M<A> ma, Function<A, B> f) {
    return m.bind(ma, a -> m.pure(f(a));
}

static <A, B> M<List<B>> mapM(Monad<M> m, ImmutableList<A> as, Function<A, M<B>> f) {
    switch (as.uncons()) {
        case Optional.empty():
            return m.pure(ImmutableList.empty());
        case Optional.of(cell):
            return m.bind(mapM(m, cell.tail, f), bs -> fmap(m, f(cell.head), b -> ImmutableList.cons(b, bs)));
    }
}
7
ricky_clarkson•

Something that doesn't help there is that that code stands no chance of actually working, as Java lacks higher kinded types.

6
friedbrice•

true dat, but the other purpose of code is to communicate an algorithm from one person to another, and that is my purpose here.

7
evincarofautumn•

Yeah in my experience it works really well to do exactly this

  • Here’s a language X that you already know

  • Here’s a problem/pattern Y expressed in that language

  • Here’s a solution to that problem / generalisation of that pattern, supposing that we had feature Z

“Thing you already know, with a small modification” (e.g. Java if it had higher-order “constructor interfaces”) is far easier to grasp than “thing you don’t know at all” (Haskell, usually)

In fact you can express this more directly in some mainstream languages like C++ already, since a template may be parameterised by a template

4
sfultongSIL•

This brings up an interesting philosophical point: is the expectation that understanding of programming concepts should come from prose or from code?

Or, even more generally, should we work to adapt computer science better to humans, or to adapt humans better to the science?

4
friedbrice•

At the risk of sounding cliché, We kinda need to do both. Natural language is imprecise, so we need a medium which allows us to be absolutely precises. And we need training in how to precisely convey an idea, since it doesn't come naturally to us meat beings. This is exactly the role of both technical jargon and of code. However, the code language doesn't have to be so noisy and verbose like the Java above: there are cleaner, clearer, more human-friendly programming languages with which we can still precisely convey ideas.

4
PL_DesignOP•

I think I need to backtrack a little bit, because I left out an important detail: It's not just that you should speak in imperative terms, but that you should also speak in concrete terms. If you showed two concrete examples of this pattern, and then extracted this interface from them, then the concept would become much more clear.

2
friedbrice•

will do! bbl

1
[deleted]•

So, from the first paragraph, I see criticism of explanations in imperative vs functional, but the evidence you present supports more of a "how to teach good" flavor of specific/example/concrete vs abstract overarching pattern rather than the paradigms.

So, I wonder if it would be just as well to teach at the level of learning-by-example first. Specifically, "wow, I have a bunch of functions that take values and produce maybes, but I have to unwrap, do case analysis, and Nothing check each time I plug them together. There must be a better way?" An then picking your other favorite type constructors to do this with (that can be unwrapped by the user so rip IO).

This also helps put aside the pure/bind problem by letting you pick good names for each type constructor as you are teaching. e.g. for Maybe `pure` is `Just` and `bind` can be `ifSomething` or `ifNotNothing` or whatever you like. For Either `pure` is `Right` and `bind` is `ifRight`.

It seems like this would fit just as well in an imperative language (using mutation for intermediate steps) or a functional one (using let-bindings for intermediate computations).

W.R.T. the second paragraph, the problem I find is that when the learner approaches the topic, we go (at least in a Haskell point of view): "well before you can learn monads, we need to teach you typeclasses." because that is the common practice. You can see all the posts about Java not having higher-kinded types/generics kinda falling for this trap.

So, just don't? like, just put the method overloading, type-class inheritance, and do-notation to the side for a while.
Honestly, the design pattern gets in the way while they are trying to learn specific types because learners think "wow, they should all behave the same if they have the same methods" and so then you have to make them un-learn that and say "no, the semantics are very different, the general algorithmic shape is just the same".

​

For the record, I taught this the wrong way. because how I understood it best was: Some data structures are good to map over, call that a Functor. When we map a curried function over a data structure, we get a data structure of functions, and sometimes we would like to "map" that again, but Functor isn't powerful enough, call that an Applicative. Sometimes we have data/value constructors we like to plug together and Functor and Applicative don't let us do that because we get `M (M a)` double wrappings, so we need a flatMap, that's a Monad. `pure/return` are^nice^default^constructors^don't^worry^about^it. So, I have a lot to learn, too, but I don't know if this is reeeeally an imperative vs functional problem unless you equate imperative with "plain, what-you-see-step-by-step-is-what-you-get" and functional as "abstraction-barrier-a-palooza", which seems to be the real problem addressed by this post.

Edit: I will address that the big problem with teaching this in Haskell vs an imperative language is IO. for IO, you kinda have to use the monadic interface to make things work because you can't crack the IO open and write it yourself. But, people like to print stuff while they learn. So, what can you do? I have no idea. Other than just gloss over it, play in the interpreter which is in the IO monad already but shhhhhh, and don't think about IO until you've made the leap from monads-by-hand to type classes

6
jvanbruegge•

I gave a talk about the core idea behind the concept a few weeks ago and at least my colleagues liked it: https://youtu.be/tlXz3aKJ6mI

3
kittensupernova•

I've been studying Haskell for about the past ~6 months, so as someone who has struggled through learning about Monads I thought I would share my thoughts. First of all, https://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/ is highly relevant here (I suspect OP read this article since he mentioned the infamous "burrito" analogy) and it explains the issue of deficits in Monad explanations far better than I could.

Speaking from my experience as a learner and person who had used imperative languages pretty much exclusively before trying to learn Haskell, I'm not sure if imperative code would have helped me significantly in understanding Monads since as many posters have noted, Monads simply are not needed as heavily in most imperative languages as they are in a language like Haskell which tightly constrains side effects. And chances are, if you are concerned about understanding Monads you are learning Haskell, in which case understanding their usage case in this particular language is highly important.

I think when I was learning about Monads I got hung up on understanding what they are, which in Haskell is simply a family of higher-kinded types which have bind and return functions defined on them and satisfy certain algebraic laws. That's it. I don't think an implementation of a Monad interface in C++/Java/etc. would have helped at all since the Monad pattern is just not as prevalent in those languages. The core problem is, understanding what monads are does very little to help you understand why they exist and what is important about them.

For me, this was a consequence of having very little experience with functional code in general. An analogous situation would be if a person was learning an imperative language for the first time and they were introduced to the concept of the Iterator before they had much experience with code that did iteration or traversal over many different kinds of data structures. In that particular case, the Iterator interface would probably make very little sense, since they would be unfamiliar with the pattern that an iterator is abstracting over.

What really helped me in my understanding was learning about the different examples of Monads and playing around with them and writing / re-writing their implementations until I really understood. Specifically, learning about Reader, Writer, State, and their derivatives was super helpful. Trying my hand at using Parsec and seeing a Parser monad in action was also a hugely beneficial thing for my understanding. Effectively, what finally got Monads to click for me was just trying to use them a lot until I saw the pattern and started to gain a bit of intuition.

There are many applications of Monads which I have yet to learn about and I still feel like I am just starting out on my Haskell journey, but I think I am now approaching the topic of Monads in a better way. I am not overly concerned about what Monads are any more than I am concerned about what iterators are in an imperative language. What is more important is what the particular implementation of the Monad is doing and what features it offers.

Anyhow, thanks for reading - I've been thinking about this topic for awhile and this finally presented an opportunity for me to share my thoughts

3

That article was great Thanks for sharing

1
HugoNikanor•

My main problem with Monads (along with Functors and what have you) is that I can’t get a grasp of exactly which part is the monad.

A list is just an ordered collection of items, a dictionary a mapping from keys to values. In neither of these cases the procedures for manipulating the structure is clearly not part of the structure. But for a Functor, which part is the functor? Is it the abstract idea of applying map? Same goes for Monads.

2
yawpitch•

Functor (and Monad and many others) are what Haskell calls typeclasses… known in languages other than Haskell (and modulo some significant details) as interfaces / traits / protocols.

So what makes a given thing a Functor? It implements fmap and it follows the Functor laws:

  1. It must preserve identity … if you fmap the identity function — aka give me back the argument I just gave you, unchanged — over a Functor you get back the Functor, unchanged.

  2. It must preserve composition … if I fmap a composed function — a function made by calling one function and then passing the result into a second function — over a Functor, the results must be the same as composing the fmap of the first function with the fmap of the second function and running that over the Functor instead l.

That’s really it… it’s a Functor if you can provide an implementation that has the functionality of a Functor and follows the Functor laws. No different, conceptually, than say Eq in Rust or the iterator protocol in Python.

Monad is more complicated (more behavior + more laws), but that’s because a Monad has to be a Functor… fundamentally, just a horrible name and a lot of baggage, poorly explained, for what is really just an interface to adhere to.

3
ebingdom•

Functors (and Monads and many others) are what Haskell calls typeclasses…

Functors and monads are not type classes, they are instances of the respective type classes. Functor and monad are type classes.

1
yawpitch•

Fair point, edited.

1
lobster_johnson•

Monad (as well as functor, applicative, etc.) is just an interface. In other words, some kind of functionality that a type provides that can also be provided by other types.

In languages like Rust, C#, Java, etc. a functor might be (pseudocode):

interface Functor<T> {
  map(f: Function<T, R>) -> Functor<R>;
}

Any type that implements Functor will be "a functor".

3
leitimmel•

But for a Functor, which part is the functor? Is it the abstract idea of applying map?

fmap, actually, and no that's not it.

The idea behind Functor is the simple observation that if I have a T<A> and know how to turn instances of A into instances of B, I can make a T<B>. In other words, the Functor typeclass describes all functions with this type signature:

T<B> whatever(T<A> input, B transform(A))

Now to answer the question: Any concrete function that fits this signature is a functor. Pick any function from any codebase, no matter what language, and if you can fill out T, A and B, you found a functor.

Monad is nothing more than a Functor with extra restrictions expressed through more functions. So if you can find a match for fmap plus one for each of the additional requirements, then those matching functions together make up the concrete monad.

In practice, few programming languages can express these concepts and those who can usually force you to explicitly declare what functions make up the monad implementation for which type, but that's for technical reasons, not because of any theoretical property.

2
ebingdom•

fmap, actually, and no that's not it.

Did you really just correct them by calling it "fmap" instead of "map? You realize that's just an arbitrary name that Haskell picked right? A different programming language could call it something else. And "map" is arguably a better name than "fmap" anyway (even many Haskellers think so).

The term "functor" comes from category theory, but the term "fmap" comes from Haskell.

4
leitimmel•

Isn't map just another arbitrary term Haskellers picked? I'm 90% certain that maths would call it either functor or f. So since we're most likely talking in a programming context, it's best to use the name that leaves the least room for misunderstandings, and I believe that's fmap, so I went with it.

1
yawpitch•

I understand your frustration — the ways in which monads are introduced in virtually every text, tutorial, and blog post are abjectly (if not objectively) terrible and, as can be seen anytime an author drifts off into the category theoretical aspects, too often lie somewhere on the spectrum of sadomasochism — but can I ask you to explain precisely what path has brought you to monads and why you feel the pressure to understand them?

I wonder if it’s at all like mine, and you’ve — perhaps prematurely — decided you need to learn Haskell?

1†
MegaIng•

What made me try to understand Monads is the fact that this subreddit doesn't shut up about them and trats them like a wonderweapon that can solve any problems.

1
yawpitch•

Well, then what brought you to this subreddit? There’s a lambda on the cover logo — first hint of an FP bias — and the description says it’s about programming language theory; well monads, for better or worse, are a fundamental part of current thought in PLT and the application of “pure” FP ideas to effect-full programs, even if other later effects systems (like algebraic effects and their handlers) end up being how we actually go about isolating those effects in production languages.

The general concept of a monad has no real relevance today for an imperative programmer who is happy to continue with imperative semantics, and as you’ve mentioned you don’t really need to understand monads to use the examples that are likely to enter an imperative-first language in the foreseeable future (ie lists, Maybe/Option and Either/Result), while the more “exotic” monads (ie Reader, State) and especially generic, user-defined monads and monad transformers really have no obvious use in imperative work, quite possibly for decades to come.

Except, that is, if you’re writing parsers, compilers, and interpreters.

So other than them coming up for discussion a lot in (usually) FP circles, why do you feel pressure to understand them when — if not learning FP or working in a fairly narrow set of domains — you don’t obviously need them?

BTW, in a nutshell monads are a structure that allows you to concatenate together functions that all happen inside some context. They’re basically a fancy form of function composition, where those functions are tagged both with a type they return and a type of effect that is needed to to compute the value of the type being returned… they’re no vunderveapon… they just solve one particularly vexing problem of typed and pure functional programming.

2
till-one•

There’s a lambda on the cover logo — first hint of an FP bias — and the description says it’s about programming language theory

I didn't notice the lambda until after about a year. And even then, so what? The logo for the Assembly programming subreddit I think represents an integrated circuit; little to do directly with assembly.

And actually, the description of this subreddit is:

programming languages, programming language theory, design, their syntax and compilers.

Nothing suggests it needs to be exclusively about FP languages, PL theory, type theory, lambda calculus, or even monads (whatever they are!). If there's a better subreddit that deals better with more practical aspects, tell me what it is and I'll give it a go.

​

monad transformers really have no obvious use in imperative work, quite possibly for decades to come.

Except, that is, if you’re writing parsers, compilers, and interpreters.

That's mainly what I code these days. I don't use monads, just plain code. Unsophisticated but my tools have usually been blazingly fast compared with alternatives, which means much more productivity. So, who cares?

4
yawpitch•

The logo for the Assembly programming subreddit I think represents an integrated circuit; little to do directly with assembly.

Ahh, but if the logo had been a JMP instruction you’d have recognized that the subreddit would tend to have a lot of low-level code discussions… just as Church’s Lambda means you are about to wade through a lot of monads.

Nothing suggests it needs to be exclusively about FP languages, PL theory, type theory, lambda calculus, or even monads (whatever they are!).

And nothing I’ve seen suggests that exclusive focus exists. But it is heavily biased towards PLT, which is heavily biased towards typed FP… which is heavily concerned with monads or at least some other solution to the problem they address.

That's mainly what I code these days. I don't use monads, just plain code. Unsophisticated but my tools have usually been blazingly fast compared with alternatives, which means much more productivity. So, who cares?

Odds are you actually are using monads, you’re just not aware of it (lists and therefore arrays are monads, thus so are strings) and not using them to full effect. Which, as I’ve tried to point out above, is absolutely fine… if you’re not writing richly typed code in a functional programming manner the you can probably ignore any discussion of monads. But if you’re writing languages, compilers, etc, specifically without leveraging them then there’s a good chance you’re doing things the hard way while also making it harder to prove your code is actually doing what you think it’s doing across all inputs.

Haskell and OCaml, especially, have proven to be very good at writing parsers that are equally fast but considerably easier to understand, validate, and modify than hand-rolled imperative versions… there’s nothing wrong with using languages for their strengths, where they’re strong.

0
MegaIng•

So your solution to the critizism by OP is to just leave the community? Ty, wont be responding to the rest.

-2
yawpitch•

Well, that’s one way to completely misread someone ans jump to [vastly incorrect] conclusions.

No, my question to the OP (and to you) is where is this pressure coming from? I know where I, personally, got it… I became more and more curious about compilers and that led to writing parsers and that led to FP and that led to typed FP and that led to monads… the pressure wasn’t, at all coming from the community, it was coming from me.

Now you then said that you feel the pressure from the community… ok, but that only makes sense if you’ve found the wrong community. Which you’ve only done if you’ve not actually got problems that typed FP and by extension monads are trying to address. If you are in the right place then your problem isn’t the pressure, that is still coming from within, the problem is finding the learning resources you want to help you.

Now at that point there’s very good reason to critique the community, as practically speaking the number of people who can communicate monads both succinctly and well (and without recourse to either category theory or problematic metaphors) is zero. Like it or not monads represent the nearest edge of research space to production-scale industrial programming language design… they’re more than 30 years old and just now reaching the awareness of industrial programmers; there literally haven’t been any non-mathematicians/non-CS-grads-or-postgrads using them, much less figuring out how to teach them, until the last few years.

So what I’m trying to suss out is literally what brought OP to monads and what can the community do to better address that arrival vector. I’m not saying “get out” at all.

2
till-one•

I thought that was currying.

2
PL_DesignOP•

I don't feel any pressure to understand monads. I just find it grating how FPers so often act like they're god's gift to software engineering while being incapable of communicating with anyone outside of their clique.

1
yawpitch•

You know the funny thing? Inside the halls of PLT research, most people seem to have come to the conclusion that monads probably aren’t the solution, precisely because monads themselves don’t compose nicely. If anyone is actually suggesting that monads are God’s gift — though I for one haven’t seen much evidence that they are suggesting that — then their God really does work in some world-class mysterious ways.

I would say that most FP cultists push hard on FP, and that Haskeller’s — especially — push on monads as their language’s abstraction for purity over effects (but even then will turn around and tell you to use an effects system library), but that they’re really not all that central to workaday programming with FP. The isolation of effects stuff, that is important (and I’m certain will become much more important), and FP is arguably the better model for maintaining that isolation around arbitrary effects, and monads represent the most well studied and nearest to delivery means of providing that isolation… but in imperative languages that’s Option / Maybe and Result / Either and as you’ve already said those are quite easy to pick up the semantics for without ever needing to know they’re monads, much less have the ability in your language to implement arbitrary monads.

5
[deleted]•

[deleted]

1
justinhj•

I’ve made tutorial videos and conference talks about introductory fp including monads and applicatives. I have two approaches depending on the audience. For imperative programmers interested in fp I usually begin with pure functions and how to compose them, what their benefits are. Then I show real life functions that have impure effects. Then I introduce functor and map, applicative and ap, monad and flatmap, not necessarily named as such, to show how you regain the ability to compose functions and have referential transparency when you use these structures and type classes. For some audiences I use the names and link them off to deeper theory. I also made a video that starts with basic category theory and explains function composition and then kleisli composition in diagrams and then in scala which is, imho, a great way to grasp what a monad is used for in fp and its link category theory. I don’t think either of these is the perfect way to teach monads but it shows that there are multiple ways, and you should tailor to the audience. Ultimately there are people that want both.

1
lbseale•

I recently learned enough Haskell to write practical code, and I completely agree that it is often taught badly. That said, I really love Haskell and wish for more people to love it too.

The thing that was really new to me was the concept of wrapper types. (Or "higher-kinded types" to use a term nobody understands). Imperative programmers have tons of experience with the most fundamental of wrapper types: lists. You can think of List as a type you can wrap around any other type. So you have a List of Ints, List of Strings, list of lists, whatever you want.

The Haskell people are great at noticing common patterns among wrapper types and abstracting them into type classes. Then they give the type classes weird confusing names.

Some fun Haskell examples:

Monoids - Wrapper types than can be empty, and concatenated together (Example: lists can be empty, and can be concatenated. Lists are in the Monoid type class)

Functors - Wrapper types that you can unwrap, do something to their contents, then rewrap (Example: think of iterating through a list and doing something with each element to create a new list. Lists are in the Functor type class)

Applicatives - Wrapper types that can contain functions to apply to other wrapper types. This is where things get weird and Haskell-y. You need to have the concept that functions can be considered values. Haskell is big on this, but many other languages have have it too. (Example: you have a list of functions, and a list of values, you can combine them by applying all the functions in the first list to all the values in the second list, to get one big list in return. Lists are in the Applicative type class)

Monads - Wrapper types that can be sequenced together. Functions that take a plain value and return a value wrapped in these types can be linked together in a chain. (Example: You have two functions that take a value and return a list. You can sequence them together by taking every value in the list resulting from the first function and feeding it into the second function. At the end, you get a big list. Lists are in the Monad type class)

It turns out that many, many things in programming follow the pattern described by the Monad type class. Haskell guys just noticed this and tried to make the language abstract enough that it's meaningful to talk about them. Most other languages have specific implementations of how to sequence things together, so it doesn't help you know that they're following a Monadic pattern.

1
lookmeat•

How do you describe the difference between red and green to someone who was born blind? How do you describe the meaning of the first word to that?

Many people never understand numbers, like the basic things. People freak out when they realize that 0.99999... == 1.

Many people never understand Monads. And that's because nothing is like Monads. You can describe some things in terms of Monads, but never Monad in terms of those things. It's something so simple and basic and trivial that it paradoxically becomes ineffable.

A Monad is any type M<?> where there's function (T) -> M<T> for any T and there's one that's (M<T>, Fn(T)->M<U>)->M<U> for any T and U. The second function could also be a method for any M<T> taking only the function.

That's it. There's nothing else. Why does that matter? Well try explaining to the always-blind kid why the difference between red and green matters. Like blind people somehow do it without colors, so why does it matter they are different? Try explaining it to an alien race that can't see light. This is where it gets complicated.

And to counter your point. Imperative programmers need to swallow their pride and realize there's many ways to see the same thing. If you define yourself an "imperative programmer" you are, by definition, someone who chooses to code badly. Not always, but sometimes. It's unavoidable, if all you have is a hammer, sometimes you will nail a screw in and ruin the board and screw. You can talk and write English perfectly well without ever using a word that has the letter e, but sometimes you'll struggle and things will be very complicated. It's perfectly fine as a personal choice, but if you want to call yourself a professional writer, you should be capable of using the letter e, even if you choose not to.

So I expect coders to learn and think in more ways. To realize that imperative programming is not "a thing", it's a weak metaphor you use to begin. Others use "functional programming". But really there's just ways to describe a problem and its computational solution, and they're all valid and work together. I'd expect programmers to learn functional programming, well enough to understand Monads, and Lenses, and Recursive Schemes. I also expect them to learn OOP, and patterns, and SOLID principles. And have the realization that you can implement Functors as Visitors that happen to return a new copy of the object they transformed, and that with this you can implement recursive schemes and describe transformations of the internals of objects in a way that is still very much tell don't ask. I expect people to learn about imperative programming, and realize the pragmatic way in which computers work. I mean there's solid Javascript tutorials for Monads in Javascript but really they're just as hard to "get".

And yes, this also means that things like Monads are actually very hard to understand. Not because they are complicated things, but because they require a lot of context, and abilities, and skills before they start making sense. You can kind of explain it, but it doesn't matter. The biggest sin is that people don't realize that, without certain background and context, you can't quite get some things. And it's not just with Monads. See how we teach trigonometry, suddenly shapes and formulas appear, and sin is really important, but most kids won't get an intuition. And many people will tell: you need to teach trigonometry like this! You should see things the way the kids do, look at their cultural background! But maybe the thing is that without map-making classes, drawing classes, etc. you just never get the necessary knowledge to even begin getting what the hell is the point of a circle and how is it not triangles, but we still use pi for both and why does any of this matter at all?

0†
rpiirp•

"Imperative programmers need to swallow their pride and realize there's many ways to see the same thing. If you define yourself an "imperative programmer" you are, by definition, someone who chooses to code badly. Not always, but sometimes. It's unavoidable"

Functional programmers need to swallow their pride and realize there's many ways to see the same thing. If you define yourself an "functional programmer" you are, by definition, someone who chooses to write inefficient code . Not always, but sometimes. It's unavoidable. .. (rest of the sermon applies both ways as well)

7
lookmeat•

I agree. There's no "one solution" there's simply a bunch of ways of doing things. But it's imperative programmers who try to argue "this is how it is" when it really isn't the case either. That's the whole point.

I didn't choose imperative because I think it's worse or better. I chose it because people came here to say "I don't have a reason to think functional, there's imperative". Had it'd been a functional programmer saying they "didn't need to learn about mutexes, because that's an imperative thing" I would have probable responded with a similar statement to yours. If you see later I do say that functional programming is a weak metaphor, as is imperative. I attack everything the same.

The fact that you assume that I am directly attacking imperative as strictly inferior, that it means I must be pro-functional (what if I like Prolog?) is the thing I am criticizing. We have made this camps of this completely irrelevant lines. There's techniques, there's solutions, there's ways to model problems. A programmer should strive to understand everything. Monads are extremely useful for someone coding in imperative programming, not because functional is better, or monads are the end-all-be-all of programming, but because some problems are best understood and solved using monads, even in imperative languages. Similarly with side-effects for functional people, sometimes it's best to think of side-effects, and have them only happen when a line actually is used. Some problems are best understood, and solved, by this. Doesn't mean short-circuiting is the end-all be-all of programming, but its useful.

Because the sermon above applies to a bunch of other things. There's so many other ways to code. What about OO? What about Erlang's many replication model? What about logical statements? What about stack-based programming? What about lazy vs eager (and yes you can have lazy imperative, that's short circuiting!)? What about Co-procedures vs threads? And each is just another tool on the toolbox. And outright thinking there's a choice, or that one is better than another completely misses the point. And that mindset can only limit our ability as programmers.

1
raiph•

Single dimensional voting is such a blunt instrument!

Here's the bit I recommend readers focus on:

A Monad is any type M<?> where there's function (T) -> M<T> for any T and there's one that's (M<T>, Fn(T)->M<U>)->M<U> for any T and U.

I urge readers to consider whether they understand it, or don't, or disagree with it. Then read and/or write replies focused on just the above excerpt.

----

If you find you just can't ignore the rest of their comment, consider this slight tweak of one part of it that I suspect may have been particularly problematic:

And to counter your point. Functional programmers need to swallow their pride and realize there's many ways to say the same thing. If you define yourself a "functional programmer" you are, by definition, someone who chooses to code badly. Not always, but sometimes. It's unavoidable, if all you have is a screwdriver (talking up FP), you may be tempted to use it as an awl, bash a nail in with the butt of the screwdriver, and ruin the board, nail, and the screwdriver.

I'm fairly confident u/lookmeat will agree with this. I think they were making a rhetorical point with their original version of the above, and will acknowledge the value of my rhetorical repartee. My confidence is grounded in my instinctual sense of what folk mean, as exemplified by the following additional excerpt, in which I've again translated a couple words:

I urge coders to learn and think in more ways. To realize that "functional programming" is not "a thing", it's a weak metaphor you use to begin. Others use "imperative programming". But really there's just ways to describe a problem and its computational solution, and they're all valid and work together.

Perhaps u/lookmeat will get what I'm saying in the above, and maybe even explicitly agree. Or perhaps they will object to my tweaking of their words, or their point. Either way, I hope they understand that, to the degree they intended their post to convey a spirit of fun and insight, I feel I got it, and hope others do too.

5
lookmeat•

I agree with the vision, it does change the core point. That is you don't challenge someone's biases by telling them "the other side has this issue", but by challenging them. But it's true in that this is universal to everyone.

Computers are not Von Newman machines, nor Turing machines, imperative doesn't quite work on it. The notion of time is hard to construct on an electronic circuit, but it's there because it's how we humans need it to work. There's no requirement for this though.

It shows, I think, the false dichotomy for a lot of people. My whole point is there's no one way to program, they all are limited and flawed in their own way. A man with only a hammer thinks everything is a nail, but that doesn't mean that the man with only a screwdriver is superior. Both are dumb for limiting themselves so. But we've internalized this so much that someone attacking "imperative superiority" must be all for "functional superiority". And that whole mindset is what I wanted to attack.

And the problem with Monads is that people try to preach it like it's critical that you understand it, and try to push it. But in reality it's no more important than OO design patterns. Very useful in some ways, but ultimately just another tool. If you don't see what's the point of it, maybe you simply haven't had a problem that needs it. And that's fine, when you do, you'll learn about it.

2
stackdynamic•

That's it. There's nothing else.

I would add that by convention, monads also satisfy the monad laws (left/right identity and associativity).

2

For the purposes of OP this is all useless arbitrary bullshit and exactly what they were complaining about. Monads exist because functions exist. Your usage of functions will follow patterns and eventually you'll be trying to use a and c and need a b in the middle to make everything fit nice.

Some folks have problems because other folks keep putting an unmotivated cart before the horse. You shouldn't be adding anything about monad laws because the comment you're responding to didn't even talk about any motivation for a monad, they were too busy talking about people's pride.

Contrary to some dumb belief, "something else matters". Often it matters a lot. People that jump into discussions of properties of types first need to swallow their pride and realize there's many ways to see the same thing.

editing -- I legitimately wish I could downvote you both more for these tone deaf responses, at first I was just assuming OP was a bit of a complainer, but now I'm kind of seeing their point of view.

6
oa74•

You shouldn't be adding anything about monad laws because the comment you're responding to didn't even talk about any motivation for a monad, they were too busy talking about people's pride.

On the contrary, the point about monad laws is relevant to the question of swallowing pride: if the claim is that learning monads is good for you because the FP way is better and you should swallow your pride and eat your proverbial vegetables, then certainly the FP crowd must be expected to eat their vegetables in turn—specifically, this means acknowledging that what we're calling monads aren't monads. Same for the non-functors used in the non-category "Hask."

But you're certainly right to shine a light on the lack of motivation behind talking about monads at all.

For imperative programmers, the monad-esque looking thing used in FP to isolate side effects is of no relevance, because imperative programmers use other means to isolate effects (such as Rust's borrow checker, or even sheer discipline). IMHO the monad-looking thing is useful for IP only because of bind, which is as easy to explain and understand as map (and need not, in the imperative setting, have anything to do with "side effects" or "purity"). And as far as I can see, the IP crowd has no problems understanding map.

That FP-style non-monads need not satisfy the monad laws provides moral justification for IP-style non-monads having nothing to do with purity or side effects—which they shouldn't, IMHO. It's fine because we're all using non-monads not because of some high-flown theoretical idea, but because they are useful in practice.

2
lookmeat•

That's a "well-behaved monad", and if you can ensure that your monad behaves in certain way, you can do some useful statistical analysis and optimizations on them.

It might seem crazy, but that's just the realities of mapping mathematical concepts to computing. Floats are not numbers, they are not comparable nor ordered (as in f+f != f+f if f = NaN, and other weirdness there), and addition, multiplication and their counterparts are not associative nor distributive.

So if you have a well-behaved Monad, you can do better optimizations, and better static analysis on it. But from the point of view of the programmer that's a promise that's not guaranteed by the description of the program (ie the type) alone. You'd need unit tests, or get a good dependent type system and a lot of patience.

3
MegaIng•

That Javascripg tutorial is great because it does another thing in addition to being in JS: it provides different examples and explains why it is usefull to have the same abstract interface for those examples.

But also, shut up with your toxic elitism. That behaviour is part of the reason FP isn't mainstream.

2
lookmeat•

I am not for Functional Programming, I am against the idea "of one true way". I called it a hammer for a reason: the man who believes in only the screw driver is just as much of a fool.

1
PL_DesignOP•

I would debate the utility of OOP and SOLID. There are lots of ideas that go around, and most of them are bad. The best ones, I've found, tend to be the ones that are easy to explain, especially in terms of what the machine will do. Ease of explanation has a medium correlation with simplicity in practice, and concrete explanations make it easy to check yourself against reality. I'm not against FP ideas being good, but the explanations are usually so terrible that my gut reaction is to dismiss them as over-complicated nonsense. For example, combinators are conceptually a more complicated abstraction over the program counter than statements are, and almost no one will ever describe them that way. They're a hard sell unless you can hit an explanation like that.

I'm sure someone's going to object to my interpretation of combinators, and that's fine, but do understand that I'm a lot more likely to ponder the opportunity cost of learning this stuff when my mental models get destroyed so often. If you guys believe in your ideas and want to spread them, then you have to develop the skill of teaching.

2
bvanevery•

If you define yourself an "imperative programmer" you are, by definition, someone who chooses to code badly.

I choose to define myself as a person who programs on real machines manufactured by industry. There is nothing pure functional about any of them, nor monadic. Attempts to commit FP to low level hardware have failed. It doesn't matter if it's theoretically possible and that people have tried. In industrial practice, the industry has spoken. Imperative is the way things actually work and is the simpler model of everything to understand about such machines. Getting people to do things in a simple way, seems to be what computer programming as an industry is really about. It's tenable, and it's scalable. You can get lots and lots of people to do it.

1
lookmeat•

There's nothing imperative about them either. Just see people who have been messed by read-write ordering when doing parallel programming.

The reality is that we simply try to model machines in a way that we can think. No one thinks about how the individual electrons in a car work, but that's very much how it works. And even then engineers have tolerances to keep their machines running on abstractions that are not perfect.

Imperative is just one model to abstract how the computer really works. Functional is another. There's no "real way the computer works" because in the end it's just an abstraction that is mantained by a machine that translates this into sets of electrons spread over certain wires at a certain time.

1
bvanevery•

Parallel programming is a specialist activity. The vast majority of deployed computers that people use in their everyday lives, are coarsely parallel at best. Single threaded is a perfectly valid way to think about many programming tasks.

Your abstraction metaphor doesn't hold up in the case of real commercial CPU architectures.

1
lookmeat•

Parallel programming is a specialist activity.

On the contrary, anyone who has ever dealt with a bug because a file being written on a machine that got shut down halfway through actually writing it to disk, has dealt with this issues.

Anyone who has dealt with networked systems has dealt with this issue.

Anyone who has ever forked something, has dealt with these issues.

Single threaded is a perfectly valid way to think about many programming tasks.

I agree, because we have an OS that gives us a space where, 90% of the time, it's fine to think like that (until you get IO, but you know you try).

But it's an abstraction, not how the computer really works. Computers haven't done "a single thing at a time" in many decades now. That's just not how machines work nowadays.

But we can act as if they do. And it works fine enough.

real commercial CPU architectures.

So x86 is not real enough?

1