9 ms·
Monads as a Programming Pattern
- kybernetikos 7y agoI think the programming pattern paradigm is the right way to explain monads (as you can tell from my own monad explanation: https://kybernetikos.com/2012/07/10/design-pattern-wrapper-with-composable-actions/ https://kybernetikos.com/2012/07/10/design-pattern-wrapper-w...) The category theory language around it is off-putting to working programmers, and many of the ways people explain it is by trying to introduce yet more terminology rather than just working with the perfectly adequate terminology that working programmers already have. I think part of it is that lots of languages don't have sufficient abstraction ability to encapsulate the monad pattern in their type system, and those that do tend to be academic focused. That doesn't mean you can't (and do) use monads in the other languages, it's just that you can't describe the whole pattern in their type systems. I was pretty sad that the discussion around javascript promises sidelined the monad pattern.
- tomstuart 7y agoIn my experience it’s easiest for people to understand monads when they’re presented as an abstract data type (e.g. what I wrote at https://codon.com/refactoring-ruby-with-monads#abstract-data-types https://codon.com/refactoring-ruby-with-monads#abstract-data...) rather than a programming pattern, because despite having “abstract” in the name, abstract data types are a relatively concrete thing that programmers already know how to use.
- keithyjohnson 7y agoI watched that talk a while ago and it was great, thank you.
- feanaro 7y ago> The category theory language around it is off-putting to working programmers Off-putting to some working programmers. I am a mathematically-minded working programmer who prefers mathematical and type theoretical explanations quite strongly since they just click for me.
- noelwelsh 7y agoThis seems a pretty good introduction to monads. There is a cliche that no-one can write a good introduction to monads. I don't think that is true. My opinion is more that monads were so far from the average programmer's experience they could not grok them. I think as more people experience particular instances of monads (mostly Futures / Promises) the mystique will wear off and eventually they will be a widely known part of the programmer's toolkit. I've seen this happen already with other language constructs such as first-class functions. ("The future is here just not evenly distributed.")
- Insanity 7y agoI like the comparison with first class functions. I do feel like they are more commonly understood nowadays than when I first started programming ~12ish years ago. I think because languages like Java are evolving towards a world where those things are common, the average programmer is 'forced' to learn those concepts.
- ajnin 7y ago"Introduction to monads" articles generally miss the mark because either 1) they insist on using Haskell syntax throughout, and this is most likely to be unfamiliar and obtuse to programmers looking for this kind of articles. Expecting people to learn a new syntax at the same time as a new concept is bound to be confusing. At least it was for me when I first came across the idea. Or 2) they go through a bunch of examples with various names and number of methods, like Maybe and Collection in this article, and the reader is supposed to infer the common structure themselves. At least this article goes through the formal definition, but I think that ideally that should come first, as it is easier to see the structure of the examples once you have established a mental model.
- dmitriid 7y ago> they insist on using Haskell syntax throughout, and this is most likely to be unfamiliar and obtuse to programmers looking for this kind of articles. Indeed, that is a big part of a problem. I find "Functional Programming Jargon" [1] extremely approachable (if you already know modern JS) even though it has been pointed out that their definitions might not be "pure"/correct enough. [1] https://github.com/hemanth/functional-programming-jargon#monad https://github.com/hemanth/functional-programming-jargon#mon...
- mbrock 7y agoI think for newbies there are two separate aspects to explain: first an intro to algebraic structures perhaps using groups as an example, then monads in particular. It’s important to emphasize that algebraic structures are abstractions or “interfaces” that let you reason with a small set of axioms, like proving stuff about all groups and writing functions polymorphic for all monads. With monads in particular I think the pure/map/join presentation is great. First explain taking “a” to “m a” and “a -> b” to “m a -> m b” and then “m (m a)” to “m a”. The examples of IO, Maybe, and [a] are great. You can also mention how JavaScript promises don’t work as monads because they have an implicit join semantics as a practical compromise.
- chongli 7y agogroups as an example Why not go all the way and teach functors and applicatives before monads? Then the student can see that monads are just a small addition built on top of the other two. Functors, in particular, are very easy to grasp despite their intimidating name. They just generalize map over arbitrary data structures: l_double : List Int -> List Int l_double xs = map (* 2) xs f_double : Functor f => f Int -> f Int f_double xs = map (* 2) xs Applicatives are a little bit trickier but once you get them, there's only a tiny jump to get to monads. Taught this way, people will realize that they don't need the full power of monads for everything. Then, when people learn about idiom brackets [1], they start to get really excited! Instead of writing this: m_add : Maybe Int -> Maybe Int -> Maybe Int m_add x y = case x of Nothing => Nothing Just x' => case y of Nothing => Nothing Just y' => Just (x' + y') You can write this: m_add' : Maybe Int -> Maybe Int -> Maybe Int m_add' x y = [| x + y |] Much better! [1] http://docs.idris-lang.org/en/latest/tutorial/interfaces.html#idiom-brackets http://docs.idris-lang.org/en/latest/tutorial/interfaces.htm...
- noelwelsh 7y agoFunctors are not useful for much on their own, so they are difficult to motivate. The Haskell formulation of applicatives doesn't make much sense outside of languages where currying is idiomatic---which is most languages. In these languages you tend to see the product / semigroupal formulation, and here applicatives become a bit trickier to explain as you need more machinery.
- namelosw 7y agoIt's hard to explain 20 years ago, but today if someone has used enough something like Reactive extension, Promise, LinQ, async/await, Optional, etc. There's a great chance to make one wonder about the similar pattern behind this, and then he can understand the abstraction very easily.
- lngnmn1 7y agohttps://karma-engineering.com/lab/wiki/MonadicVedanta https://karma-engineering.com/lab/wiki/MonadicVedanta
- toastal 7y agoI get the intention but it's even harder to understand with this Java/C# syntax. I feel like if you're gonna talk about FP you should probably highlight along the Haskell or Scala code (or similar) and provide OOP stuff for reference in case it's not clear. It ends up being so verbose that I don't many people see the 'point'.
- secure 7y agoTo me, reading examples in a syntax I’m familiar in helps me much more than reading them in a language where the concept might be elegant to express, but hard for me to get into. I think it’d be best to include examples in a number of languages with a lanugage selector, actually. That way, people who are fluent in functional languages can read that version, and others can read the one they are more fluent in.
- toastal 7y agoHere's a real life tangential example: I'm learning Thai. When I first started it was useful to show the Latin alphabet to get a grasp but it's often difficult or inaccurate to try to express that language without the system designed to express it. For instance, many romanizations lack a tone marker for tonal language, or don't show vowel length when it matters. Or for laughable example of พร, meaning 'blessing' but also a common woman's name, being traditionally romanized to 'porn' with its silent r when 'pawn' is a more neutral pronunciation for all English dialects (or /pʰɔːn/). Also, not being able to read or write the language, you will have a difficult time effectively engaging with the community and understanding the world around you. The point is, functional languages are designed to express these concepts with much, much less cruft with extra features like currying, immutability-by-default, and type classes. Yes, use your current language to get oriented, but if you're going to really learn it, pick up a proper syntax to express it.
- saghm 7y agoSure, but to extend your metaphor, using Haskell to teach non-Haskell programmers about monads is like using Thai to teach English speakers the history of Thailand; the language might be better suited for the topic, but the people you're teaching don't know it, and learning it is orthogonal to the actual thing you're trying to teach them.
- pron 7y agoI think it's important to separate the issue of what monads are/how they're used from the question when they should be used at all. While monads are very useful for working with various streams/sequences even in imperative languages, they are used in Haskell for what amounts for effects in pure-FP, and that use ("Primise" in the article) has a much better alternative in imperative languages. Arguably, it has a better alternative even in pure-FP languages (linear types). Here's a recent talk I gave on the subject: https://youtu.be/r6P0_FDr53Q https://youtu.be/r6P0_FDr53Q
- jerf 7y agoThis seems pretty good; the only thing on my mental checklist of "common monad discussion failures" is only a half-point off, because I'd suggest for: "A monad is a type that wraps an object of another type. There is no direct way to get that ‘inside’ object. Instead you ask the monad to act on it for you." that you want to add some emphasis that the monad interface itself provides no way to reach in and get the innards out, but that does not prevent specific implementations of the monad interface from providing ways of getting the insides. Obviously, Maybe X lets you get the value out if there is one, for instance. This can at least be inferred from the rest of the content in the post, since it uses types that can clearly be extracted from. It is not a requirement of implementing the monad interface on a particular type/class/whatever that there be no way to reach inside and manipulate the contents. But otherwise pretty good. (I think this was commonly screwed up in Haskell discussions because the IO monad looms so large, and does have that characteristic where you can never simply extract the inside, give or take unsafe calls. People who end up coming away from these discussions with the impression that the monads literally never let you extract the values come away with the question "What's the use of such a thing then?", to which the correct answer is indeed, yes, that's pretty useless. However, specific implementations always have some way of getting values out, be it via IO in the case of IO, direct querying in the case of List/Maybe/Option/Either, or other fancy things in the fancier implementations like STM. Controlling the extraction is generally how they implement their guarantees, if any, like for IO and STM.)
- goto11 7y agoThe problem with monads is they are horrible without some form of syntax sugar. I like the metaphor of "programmable semicolon", but in languages without some built-in support, the "semicolon" becomes repetitive boilerplate which is more code than the actual operations happening in the monad.
- fxj 7y agoI like the "do" notation in Haskell because it boils down the meaning of monads to the following: Monads let you break the functional programming paradigm that a function should have the same value each time it is called. e.g. do { x <- getInput y <- getInput return x+y } here getInput is called two times and each time it has a different value. When you now think about how this can happen in a functional language you have to understand what a monad does. The heureka moment came when I learned about the flatMap in scala which is nothing else but the bind function in haskell ("just flatmap that sXXt") and voila thats how to use monads. See the following explanation: https://medium.com/free-code-camp/demystifying-the-monad-in-scala-cc716bb6f534 https://medium.com/free-code-camp/demystifying-the-monad-in-...
- jgodbout 7y agoThis really was excellent. It actually helps with the category theory def.
- l0w 7y agowow nice post. I can also recommend Mark Seemanns take on monads: https://blog.ploeh.dk/2019/02/04/how-to-get-the-value-out-of-the-monad/ https://blog.ploeh.dk/2019/02/04/how-to-get-the-value-out-of...
- mikorym 7y agoI once googled "functional programming for category theorists" and obviously got instead to "category theory for programmers" (incidentally I use Milewski's book in this reverse way). I still have a rudimentary understanding of functional programming (apart from the canonical "it's just an implementation of lambda calculus"). And I have to say that without exercise and training one grabs at wisps and mist. In mathematics it's also like this, you often have your favorite prototypical monad, adjunction, group, set, etc. (e.g.: The adjunction Set->Set^op by powerset is a strong contender.) And I view axiomatic systems in essence as sort of list of computational rules (e.g.: transitive closure). I haven't found some idiosyncratic project to code in Haskell yet though...
- zug_zug 7y agoI guess I don't get all this "monad" stuff. This article talks about 3 types of monad. An optional, a list, and a future. However an optional is really just a list constrained to size 0 or 1. And a future is often called "not truly a monad." So I question the value of explaining this abstraction in great detail over so many articles when people struggle to come up with more than 1 concrete example of it (Lists), an example that engineers have already understood since our first month coding. Maybe somebody can speak to this more.
- rkido 7y ago> However an optional is really just a list constrained to size 0 or 1. What?
- Wildgoose 7y agoAn empty list (length zero) is "None" and a single element list is "Some" or "Just" whatever the lone element in the list is. And of course the list is constrained to not contain more than a single entry.
- feanaro 7y agoHe means that the type `Optional a` is isomorphic to the type `ListOfLengthAtMostOne a`. This is because we can pair their values up perfectly: Nothing ~ [] and Just a ~ [a].
- aeorgnoieang 7y agoThe sentence you quoted would make more sense if you interpret "is really just" like a mathematician, i.e. they're 'equivalent' in the sense that you can 'map' everything important about either in both directions. More concretely, "an optional" is something that either 'has a value' or 'does NOT have a value'. So it "is really just a list constrained to size 0 or 1" in the sense that an optional that 'does NOT have a value' is equivalent to a list of size 0, i.e. a list with no contents/members, and an optional that DOES 'have a value' is equivalent to a list of size 1, i.e. it's 'value' is the single/unique member of the list. Think of statements like 'is really just' in the sense that an integer between 0 and 255 'is really just' 8 ordered bits – they're 'equivalent' in the sense that you can map all possible values of those integers to all possible values of 8 ordered bits, and vice versa, and (importantly in a mathematical sense) in a way such that every integer is mapped to a single set of 8 ordered bit values and every set of 8 ordered bit values is mapped to a single integer. In mathematics that's often described as an 'isomorphism' which is, in working programmer terminology, just a way to convert back and forth between two sets of values 'perfectly and losslessly'.
- pjc50 7y agoI think monads highlight something underappreciated about programming, which is that different people regard very different things as "intuitive". It almost seems that modalities of thinking about programming are bigger, external things than programming itself. Certainly they're barriers to learning. Like Lisp, there seems to be about 10% of the programmer population who think "ah this is obviously the clearest way to do it" and the remaining 90% who go "huh?", and the 10% are really bad at explaining it in a way the others can grasp. The two monad explainers that really resonated with me were: - How do you deal with state in a language that would prefer to be stateless? Answer: wrap the entire external universe and all its messy state up into an object, then pass that down a chain of functions which can return "universe, but with some bytes written to the network" (IO monad) - If you have a set of objects with the same mutually pluggable connectors on both ends, you can daisy-chain them in any order like extension cables or toy train tracks. (It's a joke, but people need to recognise why "A monad is just a monoid in the category of endofunctors" is a bad explanation 99% of the time and understand how to produce better explanations)
- jagthebeetle 7y agoI think I approximately understand monads, but I find the "wrap the entire external universe" type of explanation to confuse me a bit. It's just the result of one IO computation + a way of handling / unwrapping it! When trying to map (or bind :) a monad to OOP/imperative programming, it strikes me as more straightforward to think that, e.g., the IO monad is an object that encapsulates the result of a network operation, together with some utility functions for dealing with the result and not having to deal with the unwrapping of the result. Kind of like Futures or Promises. (Now, here the real FPers will say that the comparison is flawed because of certain FP desiderata like referential transparency, but that's beyond the extent to which I've internalized monads.)
- pjc50 7y ago> result of one IO computation But for many people, they will be used to IO returning nothing, or error values they can ignore. So the case has to be made as to why you need a monad there at all. (I've just had the slightly disturbing realisation that C++ streams are also monads .. the documentation never uses this term.)
- stevefan1999 7y agoThis recalled me a joke on monads: "A monad is just a monoid in the category of endofunctors, what's the problem?" [0] [0]: http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html http://james-iry.blogspot.com/2009/05/brief-incomplete-and-m..., 1990 Haskell entry
- hardwaresofton 7y agoAnother really good functional (as in related to how they work) explanation of monads: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html http://adit.io/posts/2013-04-17-functors,_applicatives,_and_... It's somewhat more than a monad explanation -- it covers functors and applicatives, and is somewhat haskell specific, but it was one of the guides that really clicked for me when I was trying to grok monads