1. 9

  2. 2

    This is a dangerous one… When you start internalizing knowledge like this, then you’re at risk of using the title phrase, and then folks will stop listening to you. (I once had this experience, advocating that we should write a monad-based test framework for exhaustively driving a state machine, to make sure we were robust against any races. As soon as I used the word “monad”, folks stopped listening :-(.)

    1. 1

      Haha, yup. I don’t write functional code at work, but everyone once in a while I run into a good use of a monad (especially the maybe monad). As soon as I get halfway done with implementing the monadic solution, I realize that it will be much harder for my team to read/use, so I find a less fun but more pragmatic solution.

      I am fairly new to functional programming and monads, so my hope is that as I gain more experience, I will be able to find better use cases and will be able to be more convincing in their application.

      1. 4

        I am fairly new to functional programming and monads, so my hope is that as I gain more experience, I will be able to find better use cases and will be able to be more convincing in their application.

        You might find the opposite is true ;)

        IME the trouble with writing functional code in non-functional languages is you hit the limits of good taste pretty fast. map and filter work great… until you need to chain a bunch of them and build up a lot of intermediate lists, or you need to shove everything into some other data structure, or you need to mutate some global and now you need to inject that, or… Sometimes you just end up writing some convoluted reduce when a regular for-loop would have done you just fine. (I’ve lost count of the number of times I’ve seen a reduce in JS that mutates its accumulator, which is usually an object because – surprise! – the person really wanted map but that gave them an array. But then the alternative would be to instantiate a new object with every iteration of the reduce… If you find yourself doing this, please just write a for-loop, or use .forEach if you must.)

        As for monads, everyone has the lightbulb moment when they finally grok them and start seeing them everywhere. What they forget is that monads are so great in Haskell because they’re a great fit for Haskell. It turns out they’re well supported by Haskell’s features (typeclasses, HKTs, do-notation), and conversely they support programming ergonomically in the face of Haskell’s restrictions (purity and laziness). Once you go back to Python or Go, you have neither the language-level support nor the motivating constraints.

        I’ve seen plenty of people write some kind of Maybe monad in one of those languages and make a complete hash of it, when they were better off just handling nulls or errors in the most idiomatic way for that language. And sure, JS async/await and generators look like specialized versions of do-notation, but there’s no way to generalize that syntax to arbitrary “monadic” types. You’ve defined bind a bunch of times on a bunch of types, but there’s no way to unify them. (In fact you see this in the stdlib of Elm, which is a close cousin of Haskell but lacks the advanced type features necessary to program with generalized monads. Plenty of people have asked for typeclasses and HKTs in that language, but Elm’s designers would prefer to leave those concepts out for sake of approachability. Arguably this works well enough as a language pattern, but it’s a bit frustrating when the unifying piece is missing.)

        My very belabored point is that it’s one thing to point at some problem and say “hey, that looks like a monad,” but it’s another to actually come up with a solution that leverages that fact. Unless you’re writing Haskell, then go crazy :)

        1. 1

          Thanks for the advice! It will probably save me a few hours in trying to solve a problem in a non-language idiomatic way. I will keep this in mind the next time I see a problem that can be solved in a fancy/fun functional way in a non-functional language. Idiomatic solutions exist for a good reason, even if they are not as exciting.

          This is also a good motivation to use functional languages for more of my personal projects, so that I can scratch the itch of using abstract mathematical concepts to solve code problems.

          1. 1

            No problem! And that’s not to say that FP ideas don’t have a place in less-functional languages; you get a lot of mileage from preferring immutability (or localizing mutation when you can’t), pushing side effects out to the edges of your application, and good ol’ map/filter. And as for idioms, iterators and comprehensions in Python let you express a lot of things functionally albeit Pythonically; I’m not super up on Java but I understand they’ve been introducing similar things like streams. YMMV in other languages – I’ve mostly written Go at my last two $EMPLOYERs, which is delightfully boring and eliminates almost all temptation to get cute. If I had to use it back when I was really into FPing all the things, I’d have hated it.

            I want to be clear also that I don’t think being able to identify a monad/functor/monoid/blah outside a Haskellish context is useless either, but it’s better to treat it as a “design pattern” than as an instance of a language-pervasive interface. And design patterns, whether they’re OO or functional, come about when a language doesn’t have the expressive power to capture those patterns in abstractions. This isn’t a bad thing, it just means you’ll have to judge whether it’s the right call for your particular problem, and reinvent the wheel each time you do it.

      2. 1

        The best explanation for “monad” is I’ve heard is that they’re “programmable semicolons”. You can say “imagine there’s something like C# using block, but where the semicolon does some useful stuff, allowing you to chain statements in a natural way, perhaps a ‘write-file’ context where every statement is a file writing operation”. That’s going to make the concept easy to understand. You can give more examples like the Maybe monad or a list monad. There’s so much of an aura around this word that when people actually learn it, they’re surprised how simple it is.

        I’m certain it fits into a broader category theory / functional programming structure in a nice way. But that’s not quite relevant to a basic understanding of the concept that may help people see mundane code differently.

        1. 2

          The programmable semicolon analogy isn’t too far off, but I don’t think it gives the right intuition to an outsider. Here’s my attempt at an explanation of what people probably mean by “programmable semicolon”.

          Imagine a sequence of statements in an imperative program, each line defining a new variable. Now mentally transform that body of code so that the first line doesn’t define a new variable, instead the value assigned to that variable gets passed to a new function containing the rest of the statements and you just return whatever that function returns. Now do this for all the lines so that there are no variable definions, but N nested function calls. When you think of a sequence of statements like this, the variable assignment + the semicolon becomes a special syntax that means this nested function call thingie. Now the monad abstraction allows you to customise how those arguments get passed to those nested “rest of the body” functions. So for instance, if you’re talking about the monad instance for lists, every line defines a list shaped value and instead of passing the whole list to “the rest” function, you call “the rest” with each element in the list separately. Each “rest” call will return a list and you return all those resulting lists after concatenating them.

          I don’t think this explanation is necessarily a good introduction to monads, but I hope it demystifies what a “programmable semicolon” might mean. And what exactly the monad abstraction allows you to customise.

          1. 2

            Thank you! I think your explanation is great. It would make a good second step once they get the basic idea of what you can do with a monad.

            I think it’s possible for us as a community to come up with simple explanations for all sorts of concepts. It’s ungenerous and unwelcoming otherwise.