8 ms·
The “What Are Monads?” Fallacy
- fubarred 12y agoThere's a bigger issue: using computational and mathematical complexity that is confusing to large swaths of mere mortal coders. This is sadly why languages, to pick on some easy target religions like PHP and so on have proliferated. Sure it's possible to reduce LoC and look clever with ever more complex representations of computational structures, but it's another thing to write supportable code that's straightforward to debug, improve and maintain. If someone wants to use a "research" language in production and/or want perpetual job security, there are plenty of languages for those requirements too. (There's never a perfect Turing power language for anything, only better fits than others. Clarity for a new-hire reader should be most valued goal after functionality.)
- noelwelsh 12y agoA monad is just a code pattern. If you try to program in a style that minimises mutate state you'll probably end up inventing something like a monad. Heck, jQuery is almost a monad -- it's not a true monad but it is similar in concept. Monads are not a big deal. Indeed they are so much of a not big deal that I expect this is a big part of the problem learning monads. People expect them to be something difficult and exotic, and go looking for hidden depths where there aren't any. I know I did when I first learned them. Eventually you break through and realise there is no hidden mystery. It's just a data type with a few functions, and they happen to be usable in a wide variety of contexts. That's what this blog post is trying to explain. Finally, you can teach monads to new hires in a very short period of time. We do in our training courses.
- phamilton 12y agoI like this post because it describes specific monads and why they are useful. If I were to recommend a basic path to understanding monads, I'd say do this: 1. Understand `map` in the context of lists. This can be done in Ruby or Scala very easily if you don't want to get into Haskell directly. 2. Learn about the Maybe monad (or Option in scala). Understand what mapping an Option or a Maybe means. A simplified explanation is to compare it to a list of either length 1 or 0. 3. Learn about the IO monad in Haskell. Understand how IO monads can compose. At this point, it's ok to just say magic happens and the compiler then cause a single IO monad (the result of composing multiple IO monads) assigned to main to execute and the side effects will happen. At that point, you've got a general understanding of how to use monads. It will actually take you pretty far. One key point that might be a hangup is going from `map` meaning "apply this function to every element and return a new list of the results" to `>>=` meaning "apply this function as dictated by the monad and return the result". It's probably good enough to acknowledge that `map` is a simplified case of `>>=`. In any case, as the linked article says here, you should be able to understand specific Monads fairly quickly. Perhaps truly understanding monads means knowing when to create new monadic structures but that's hardly a prerequisite to being productive with monads.
- dllthomas 12y agoI think your description here is likely to be tremendously confusing, and I don't recommend anyone who doesn't already grok functors and monads read it...
- phamilton 12y agoMy point is that you can actively use Lists and Options in Scala and be using monads without having any idea what a functor or monad is. I believe that's a similar point to the article. It's easier to understand how to use specific monads than to understand the mathematical definitions of functors and monads. Knowing `fmap` is essentially distributive isn't necessary to use functors and get a feel for what they provide.
- dllthomas 12y agoI understood your point. I think you were unclear about what behavior belongs to monad, in a way that will confuse newcomers. "map" (or "fmap") belongs to Functor; monads have it because they are functors. I agree that starting with the laws doesn't necessarily makes the most sense, and that they're not necessary (though can be valuable) for use of these things... but you do need to introduce the laws early enough that they're understood by the time someone goes to write their own instances of the relevant typeclasses - otherwise they're going to get bit by something that assumes the laws hold, and they're not going to understand why.
- vertex-four 12y agoHaskell's IO monad is decidedly not useful outside the context of Haskell and pure functional programming, both of which I do not find fun. Are monads in general useful outside that context - for example, in mostly-imperative languages like Rust?
- olavk 12y agoThe IO and State monads is specifically to solve the problem of side effects in a purely functional language, so they are not really relevant in other languages. But other uses of monads, like say monadic parsers could be useful in other languages. And list comprehensions is also based on a monad in Haskell - this functionality is clearly useful in many other languages. But without some kind of syntactic sugar (like do-notation in Haskell) the use of monads becomes far to cumbersome, and it ends up being easier to solve the same problem in an ad-hoc manner.
- psibi 12y ago> jQuery is almost a monad Curious to know how as I have seen lots of people claiming jQuery to be Monad ? jQuery neither has any laws to govern it nor it has bind or inject interface.
- fubarred 12y agoYup, people will still ask "WTF are monads?" just like they'll soon ask "WTF are (combinators|arrows|...)?", and there will inevitably be more tutorials, as per [0]. :) It's clarity of explanation that makes anything teachable &| learnable. [0] https://www.youtube.com/watch?v=xKRndVoo2ms https://www.youtube.com/watch?v=xKRndVoo2ms All signs of superhuman nature appear in man as illness or insanity. ~ Nietzsche
- olavk 12y agoI think it is more confusing than enlightening to claim that jQuery is 'almost' a monad.
- ericelliott 12y agoThis is a terrible argument. I agree with the sentiment that code should be easy to read and debug for mere "mortals", but functional programming can radically simplify the process of understanding programs in many ways. Don't fear the monad! See: https://medium.com/javascript-scene/the-two-pillars-of-javascript-pt-2-functional-programming-a63aa53a41a4 https://medium.com/javascript-scene/the-two-pillars-of-javas...
- chriswarbo 12y agoThere are different types of complexity. Whilst it's easy to see what a simple line of code is doing, it's not necessarily easy to see why it's being done, or to understand how a codebase made of simple lines works. An extreme example of this is machine code, where every instruction is incredibly simple, but trying to work out what the whole thing's doing in order to debug it can be very hard. As a mere mortal, I appreciate monads and the simplicity they bring to my code.
- flebron 12y agoIt's kind of ironic that the author states "In his article, Byorgey hints at what I'm going to say, but I think it deserves to be said again, with slightly different words.", which is essentially what monad tutorials say about other monad tutorials :)
- justinpombrio 12y agoThe 'The "What are Monads?" Fallacy' Fallacy?
- crispweed 12y agoYeah, it's kind of like: There's a problem with too many people making blog posts with dubious metaphors for monads, so here's a blog post with another dubious metaphor for monads. Quite liked the post, nevertheless..
- michaelsbradley 12y agoHaving also struggled with Monads before having an aha! moment, I've tried on occasion to help boil it down for those on the search for understanding. The hardest thing to get is that Monads aren't quite as "tangible" (not sure of a better word) as concepts like variable, object, property, instance, function, method. They are essentially relational and require something of a mental leap akin to coming to grips with how JavaScript's event loop is bound up in the control flow of your code, which otherwise proceeds from top to bottom of a .js file; or the rules governing closures and asynchronous function evaluation in this or that language. The following is reprised from one of my HN comments in 2013. ------- Monads are programming patterns which relate a set of values to a set of of functions – that's pretty much it! For any monad X, we can say that a function is monadic if it returns a value in the X Monad. The set of values are precisely those such that the following three Monad Laws[1] hold: [ Using JavaScript syntax, they can be approximated as... ] Left Identity mBind( mReturn(a), f ) == f(a); // => true Right Identity mBind( mReturn(a), mReturn ) == mReturn(a); // => true Associativity mBind( mBind( mReturn(a), f ), g ) == mBind( mReturn(a), function(a) { return mBind( f(a), g ); } ); // => true Above, `a` is (basically) any value, while `f` and `g` are any monadic functions for the same Monad. The definitions of `mBind` and `mReturn` will vary depending on the Monad, but the same laws (and sometimes additional properties) hold for each group of things taken as a whole – the monadic values, monadic functions, and the `mBind` and `mReturn` pair for those values and functions. As you can see, there is nothing special that requires a statically typed language. That being said, if your language is statically typed, and even more so if its type system has advanced capabilities (e.g. Haskell's type system), then the relationships between the monadic values and functions can be leveraged to do a number of useful things, e.g. catching a host of bugs at compile time (though that's true regardless of Monads). If your language is dynamically typed, then you won't get those extra benefits, but you can still take advantage of the fact that Monads can abstract away a great deal of plumbing between various pieces of your program. Sometimes the Monad patterns will appear in a language under another name, or will be used "under the hood" to implement a particular API. Clojure/Script's `let` for example is essentially the Identity Monad; and its `for` is very much akin to the List Monad (the same is true for list comprehensions in other languages). The core.async library involves an inspiring application of the State Monad pattern[2]. [1] http://en.wikipedia.org/wiki/Monad_(functional_programming)#Monad_laws http://en.wikipedia.org/wiki/Monad_(functional_programming)#... [2] https://github.com/clojure/core.async/blob/master/src/main/clojure/clojure/core/async/impl/ioc_macros.clj#L56 https://github.com/clojure/core.async/blob/master/src/main/c... [&] https://github.com/clojure/core.async/blob/master/src/main/clojure/cljs/core/async/impl/ioc_macros.clj#L46 https://github.com/clojure/core.async/blob/master/src/main/c...
- sillysaurus3 12y agoThe fact that no one is able to show a canonical, succinct example of a monad makes me suspect it's multiple concepts under a single banner. But I haven't spent much time trying to understand what one is. EDIT: Thanks for the responses. I'd love to study any resources that you found useful when learning Monads. Do you know of any?
- deleted 12y ago[deleted]
- eru 12y agoThe mother of all monads is the continuation monad. That's the most canonical monads that can implement all other monads. It's rather too abstract as an example to beginners, though.
- noelwelsh 12y agoThe thing that makes monads so powerful is they can be used in a such a wide variety of situations. You can model control flow with the continuation monad, concurrency with the future monad, and error handling with the either monad. Three very different domains but only one abstraction to learn! I can also give you a very succinct definition of a monad, or a succinct example, but I doubt you'd understand them until you've seen many examples in different domains. (If you want examples or defn, there are many web pages that have them.)
- edwintorok 12y agoWhat helped me understand monads was starting from the notion of 'promise' (or future), and how you can chain those: https://en.wikipedia.org/wiki/Futures_and_promises https://en.wikipedia.org/wiki/Futures_and_promises. Once you have that concept it is easier to now learn what a monad is in the scope of a monadic concurrency library. Similarly learning about the either monad might be easier if you start from the concept of chaining error handling, as in this tutorial: http://fsharpforfunandprofit.com/posts/recipe-part2/ http://fsharpforfunandprofit.com/posts/recipe-part2/, and then show how it can be done with monads. But I agree with the 'monad tutorial fallacy' that you'll only learn it once you actually try to write code using it and see if it works the way you thought it would.
- logicallee 12y agoI'm really sorry author, but this is how I read your post: >This is like someone new to a language with regular expressions asking what a regular expression, is, or someone new to object oriented programming asking what a class or object is, or someone new to functional programming asking what functional programming is, or someone new to node.js asking what node.js is, or someone new to programming asking what a program is. >What's _wrong_ with you people? These are horrible questions. I refuse to answer them, and you should know better than to ask. Yes, I could illustrate, but why should I? >I prefer to just explain that it's a question that won't help you in the slightest. I'm not going to illustrate something that will, or give you any examples. >Just do it. The important thing is, not in front of me. Author, you could vastly improve your blog post by actually doing the teaching instead of sending the student off.
- tome 12y agoThe article is criticizing people who explain monads badly, not people who are trying to learn about monads.
- logicallee 12y agoYou didn't really read it carefully then. It's criticizing the question. You can't "criticize people who explain monads badly" without explaining monads well. Think about it. This is like me criticizing how classes are taught, without offering my version or showing how they should be taught. Come on. I'm afraid this post is completely without any value. It's an outline for the post that the author should write, when the author figures out how to teach monads (which don't exist in all languages) properly. Building up from several usage cases, per the author's outline. The only problem with the outline is that it's notes to self on how to teach monads well. Well, go ahead then. Put your money where your mouth is and teach people who have never used a monad how to use a monad. Which is why they're asking.
- Scea91 12y agoI don't agree with the blog post since the same argument could be made for any design pattern. There is nothing wrong at trying to understand things at a higher level of abstraction.
- stormbrew 12y agoWell, sure. But reading the GoF book as your introduction to programming wouldn't exactly be all that useful. Once you've actually experienced some programming and seen the patterns live and breathe it becomes a much more useful book. Likewise, if you've never actually worked with monads the high level explanations aren't terribly useful.
- kqr 12y agoI'm probably one of those rare programmers who has never had any real experience with OOP. I can tell you that reading about design patterns makes my head spin. I've been able to understand a few design patterns, but that has been because I've had an actual, practical problem in my code and the design pattern could be applied to solve it. Take as an example the following paragraph from Wikipedia: > The essence of the Mediator Pattern is to "define an object that encapsulates how a set of objects interact". It promotes loose coupling by keeping objects from referring to each other explicitly, and it allows their interaction to be varied independently. Client classes can use the mediator to send messages to other clients, and can receive messages from other clients via an event on the mediator class. I have no idea what that means and how and when I apply it in practise. It's just a bunch of abstract mumbo-jumbo, like an explanation of "what a monad is" will be. So I still hold that the abstract description comes after you learn how to use the thing, not before it.
- NateDad 12y agoFWIW, I have done a ton of OOP experience, and I have trouble understanding most descriptions of design patterns. One you see them implemented, you generally think, oh, well, duh, I've done that before. I think the main problem is that most programmers are terrible at explaining things, and should just stick to code examples, because they're usually a lot easier for other programmers to understand.
- crispweed 12y agoIs there an rss feed for this blog?
- kqr 12y agoUnfortunately no, but that's in the works. Now that several people have requested it, I've bumped it up on my list of priorities.
- dschiptsov 12y agoEach narcissistic blogger should at least once write about monads - http://karma-engineering.com/lab/wiki/Monads2 http://karma-engineering.com/lab/wiki/Monads2
- olavk 12y agoI think it is a cultural mismatch. The Haskell community seem to be very influenced by mathematicians and their way of describing everything as abstractly a possible. You can even see it in code examples where it is pretty common to see arbitrary one-letter variable names. The software development community is by and large much more pragmatic and focused on solving real-world problems. They have an easier time understanding concepts and code examples when explained in the context of real-world problems. So when the Haskell people try to explain monads, they start with the abstract definition, the monadic laws and so on, and they focus on explaining the concept of a monad, rather than what they are practially useful for. Even the many infamous monad-metaphors (its like a spacesuit! No its like a burrito!) try to find a metaphor for the abstract concept of a monad, not for any practical use. For the typical developer this might seem like a lot of mathematical wanking for little benefit. The language F# seem to have a more 'pragmatic' culture. 'Monads' are called 'computation builders' in F#, and the equivalent to do-notation is called 'computation expression'. Yeah it is just naming, but it shows a focus on the use of monads rather than on the underlying mathematical concept. A typical F# example of the use of monads - sorry, computation builders is the Async monad: let AsyncHttp(url:string) = async { let req = WebRequest.Create(url) let! rsp = req.GetResponseAsync() use stream = rsp.GetResponseStream() use reader = new System.IO.StreamReader(stream) return reader.ReadToEnd() } This example fetches a resource over http without blocking. It is immediately obvious why this is useful. After all, similar functionality was recently added to C# through the keywords 'async' and 'await'. But in F# it didn't require the addition of new keywords but could be implemented as a library, due to support for monads in the language.
- chriswarbo 12y ago> You can even see it in code examples where it is pretty common to see arbitrary one-letter variable names. This is usually because Haskell functions are really short. For example: compose f g x = f (g x) We could write this with more descriptive variable names, but there are only 3 variables and they're only in scope for one very tiny line: compose outerFunction innerFunction argumentForInnerFunction = outerFunction (innerFunction argumentForInnerFunction) There are certain conventions as well; if a variable must be present, but isn't used, it gets called `_`: first (x, _) = x Lists tend to get a plural "s" on their name: elem x xs = any (map (x ==) xs) > So when the Haskell people try to explain monads, they start with the abstract definition, the monadic laws and so on, and they focus on explaining the concept of a monad, rather than what they are practially useful for. For the typical developer this might seem like a lot of mathematical wanking for little benefit. This is because functional programmers tends to spend the most time on writing generic, abstract, re-usable libraries. Particular applications are just thin veneers on top of these libraries. For example, I might spend most of my time writing generic, abstract code to deal with tuples. To make an application, I can just pick and choose the library code I want to use: type Name = String type Address = String type Quantity = Int type OrderID = Int type SKU = Int type Customer = Tuple Name Address type Order = [Tuple SKU Quantity] type Invoice = Tuple Customer Order Given that I'm using Tuples for Orders, Invoices and Customers, the time I spent writing abstract code to deal with tuples wasn't "a lot of mathematical wanking for little benefit". The time spent writing generic, abstract monad libraries isn't wasted either; by picking and choosing which monads to use, our application can automatically get error-handing, non-determinism, backtracking, exceptions, state, software transactional memory, etc.
- hmexx 12y agoI've never really done anything seriously in Haskell, but this is how I feel monads could be explained through example. Let me know what you guys think. If it's good I'll add it to the stackoverflow question on Monads. --- We know how operators (add, substract, multiply,...) work on integers. example: 5 + 6 = 11. Now let's add some extra state/dimension to integers. Let's assume this extra state is ROCK, PAPER or SCISSORS. Here are possible values: 5_ROCK 6_PAPER 999_SCISSOR ...etc... --- What is the result of 5_ROCK + 6_PAPER ? There is no answer unless someone defines it. That's what a monad does. In this case I am deciding that 5_ROCK + 6_PAPER = 11_PAPER The way MY monad works is to use the operator on the integer portion ( 5 + 6) and then use the winner of ROCK, PAPER, SCISSORS game as the non-integer portion. 3_ROCK * 4_SCISSOR = 12_ROCK 8_SCISSOR / 2_PAPER = 4_SCISSOR ...etc...
- tome 12y agoSo what is `return` (also known as `pure` or `unit`) and does it satisfy the monad laws?
- dllthomas 12y agoI think you might be confused, because this really doesn't touch on monads at all. You can implement it by just defining an instance of the Num typeclass for your new data type: data RPS = Rock | Paper | Scissors data IRps = IRps Integer RPS fight Rock Paper = Paper fight Rock Scissors = Rock fight Paper Scissors = Scissors fight Rock Rock = Rock fight Paper Paper = Paper fight Scissors Scissors = Scissors fight x y = fight y x instance Num IRps where IRps x x' + IRps y y' = IRps (x + y) (fight x' y') Of course, with the lack of associativity, I'd weakly discourage a Num instance too, but that's not adhered to perfectly already.
- jamesfisher 12y agoFor the same reason, we should not say, "use the IO monad to do output". We should just say, "use IO to do output". You may not even need the monad instance for IO -- for example, you might just use its functor instance.
- eru 12y agoOr just use the IO combinators directly (if there were exposed outside the typeclass instances).
- jamesfisher 12y agoOf course! It turns out that GHC implements the Monad instance for IO by using `returnIO` and `bindIO` in `GHC.Base`. I played around with these as a mini demonstration of using IO in Haskell without any mention of monads: https://gist.github.com/jameshfisher/1d735b5267e8f848b280 https://gist.github.com/jameshfisher/1d735b5267e8f848b280
- dllthomas 12y agoIt's cute, but I think it's misleading to call it "non-monadic" - you're calling the same functions in the same way, just not through the Monad typeclass.
- bsaul 12y agoi think i got what functors are in category theory ( aka morphisms between categories , IIUC ), but i can't understand what it is in haskell. There's a link missing in my head between category theory and programming language. I also couldn't find a post or a video that would explain what monads are in cateogy theory.
- kqr 12y agoWhen the type is some sort of container type (Maybe, Either, List, Tuple and so on), the functor interface gives you a function that will change the contents of the container, while preserving the container structure. Example: fmap (*3) (Just 12) === Just 36 When the type is some sort of computation type (IO, Promise, STM, Parser, unwrapped Reader), the functor interface gives you a function that will take a function and compose it with the computation. In other words, the result of the computation will be passed through your specified function before it is finally returned. Example: fmap (+5) (getPromise age) -- promise age is filled with 43 and -- the fmap call will return 48
- dllthomas 12y agoFirst, all functors in Haskell are endofunctors in Hask. They map Haskell types to other Haskell types, and Haskell functions to other Haskell functions. A functor in Category Theory has two parts - a map of the objects and a map of the arrows. In Haskell, when you create a Functor instance, you are saying "this type constructor is the map for objects; here's how you map the arrows". It's typically not the best place to start, but since you asked - a monad, in category theory, is a functor plus two natural transformations. In Haskell, the functor is the type constructor plus fmap from the Functor instance. The natural transformations are return (eta, in the math) and join (mu). Haskell programmers have historically found it more useful to talk about bind than join, but in the presence of fmap you can implement either from the other, so there's not much theoretical difference: join m = bind m id bind m f = join (fmap f m)
- bsaul 12y agoThanks for that answer. I first thought monads were a completely different beast than functors, but considering the small number of fundamental objects category theory deals with, i had a hard time finding out what.
- toddkaufmann 12y agoWho's on first ? No results found for "abbott and costello explain monads".