11 ms·
I detest how everyone always takes monads, a quite simple concept, and completely butchers the explanation. I think it's likely in large part a reason for a lac
by debacle 11y ago
I detest how everyone always takes monads, a quite simple concept, and completely butchers the explanation. I think it's likely in large part a reason for a lack of their acceptance and use.
- braythwayt 11y agoCrockford’s Paradox: Once you understand and fully appreciate Monads, you lose the ability to explain them to other people.
- bunderbunder 11y agoThis might actually be generalizable to any design pattern of sufficient utility.
- pekk 11y agoIf you can't explain it, it's probably not such a great idea as you think it is.
- stingraycharles 11y agoI think that depends very much upon the audience. There are quite a few great ideas out there that are difficult to explain: try explaining the theory of general relativity to your mother, for example.
- Lewton 11y agohttp://www.amazon.co.uk/Why-Does-mc2-Brian-Cox/dp/0306819112 http://www.amazon.co.uk/Why-Does-mc2-Brian-Cox/dp/0306819112
- dllthomas 11y agoMy mother is technical and well educated. She probably understands General Relativity better than I do.
- pekk 11y ago"I can't explain it" does not depend on the audience, it depends on your ability to understand and explain it. This is why I didn't say "I can't explain it to two-year-olds".
- jdeisenberg 11y agoIf it were truly a simple concept, it might not need so many tutorials and explanations.
- codygman 11y agoPerhaps they are much simpler than everyone believes they are and the tutorials are the result of people overcomplicating them.
- dack 11y agoIt still begs the question why monads specifically have this problem, and so many more concepts don't.
- WillPostForFood 11y agoIf we can't even understand what "begging the question" means, how can we understand monads? http://begthequestion.info/ http://begthequestion.info/
- codygman 11y agoI'm drawing a blank for now, but I'm almost positive I've came across this phenomenon for many things before.
- tokipin 11y agoI think it's because they solve a problem that people don't realize they have (and truth be told maybe they don't really have it), so the learning process has no motivation. They solve a major problem that Haskell has (namely circumventing a type-anal non-strict compiler), so they're a big deal in Haskell, whereas in imperative languages straightforward code works just fine. More to the point, compared to the case of for loops vs `map`, the use case for this kind of function chaining doesn't appear all that often (nor is it as syntactically localized as a for loop) so it's not as easy to abstract. I.e. the underlying pattern is not as pronounced, so it's not really recognized as a pattern/problem/something to DRY in the first place.
- theophrastus 11y agoI'd say you're now almost guilt-edged required to provide us with your simple explanation here. But i'll warn you, i've been nicely flummoxed by competing 'simple' explanations in this forum previously: "A Monad is an object whose methods return monads.", "They're simply a highly specific way of chaining operations together.", "You know how jQuery methods can string out multiple methods with periods? That's what monads are.", "Monads are merely an analogy to control flow what abstract data types are to data."
- sportanova 11y agoA monad has two operations - bind and return - and they're used for chaining
- theophrastus 11y agoNow i've got N+1 'simple' explanations which I can use to confuse me [wink]. "bind" and "return" you say? so they're quite like a socket library?
- sportanova 11y agoha well it's more like a burrito that has two operations
- im3w1l 11y agoThose names are not very good. return takes a value and "monadifies" it. If you have function that takes a normal argument and emits a monadified value (e.g. int -> monad string). Then bind can transform your function so that it takes a monadified argument (e.g. monad int -> monad string) instead. return and bind satisfy three laws: 1. If you have function that takes a normal argument and emits a monadified value. You could apply it to some argument right. Or you could monadify the argument with return, and then use bind on the function so it will accept the monadified argument. These two ways of using the function give the same result. 2. Remember how return takes an argument, and emits a monadified value? That means we can bind it! The second law states that the resulting function is just the identity for monadified stuff. 3. Lets say you have two functions f (e.g. float -> monad string) and g (e.g. string -> monad int) from normal argument to monadified value. We could bind them both and then compose them. g_ = bind(g) f_ = bind(f) g_f_(x) = g_(f_(x)) Or we could imagine instead binding only the second function, then composing the functions, and then monadifying the composition. g_ = bind(g) g_f(x) = g_(f(x)) g_f_ = bind(g_f) Law 3 states that these two procedures yield the same g_f_
- crimsonalucard 11y agoThe simplicity is an illusion. While monads are not complex, the concept is very abstract relative to the imperative languages most people program in. Think about it. In python there's really no syntactic sugar to place something in a container, so the concept doesn't really exist in python until you invent it using other python language primitives. So when you tell a python guy about a burrito, he really has no language analogue to think about. When I tried to learning about the maybe monad, I looked up a tutorial that taught it in python... Big mistake. I was thinking why the hell would people go out of there way to wrap that shit up in a burrito when they can just do a goddamn try exception... It made no sense to me, and even now, it still doesn't make sense for python. Only when I learned about haskell did I realize how the maybe monad makes sense for haskell. I think most people learn about monads through haskell or any other language with a similar type system. It'd be interesting to hear peoples' experiences about how they grokked the concept.
- oahziur 11y agoI think Maybe monad could be really useful in Swift (Apple's new language). Some people use it for chaining up the optional type. However, I found that the lack of functional feature such as curried parameters in swift makes it less useful.
- yoz-y 11y agoYou can write a Swift function with curried parameters. func addMultiple(#a: Int)(b: Int)(c: Int) -> Int { return a + b + c } It can not auto-curry parameters, but at worst you can wrap functions in their curried couter-parts.
- dllthomas 11y agoInteresting. Does addMultiple(7) bind a or c?
- dschiptsov 11y agoOK, go on, explain what else it is, rather than accidental convention of how to ensure an order of evaluation of a pair of expressions in a particular lazy pure-functional language? Erlang, for example, being functional but strict language, requires no monads. So does Standard ML. In these languages a monads would be a useless, redundant abstraction which will only clutter the code.
- pimeys 11y agoI've been using Monads in Scala for not throwing wild exceptions, but still being able to stop the computation immediately if needed. For example you want to validate things with a Validation[A] type, which can either be Valid or Invalid. The binding function in Scala is called flatMap, so flatMap for a Valid value returns a lambda having the included value as the parameter and Invalid doesn't have a function call, so the flatMap operation stops. Example: Valid("userId").flatMap(userId => Invalid("address").map(address => User(id = userId, address = address)) would stop for the address validation, because the flatMap for a Validation is specified as: def flatMap[B](f: A => Validation[B]): Validation[B] = this match { case Valid(value) => f(value) case Invalid(value) => Invalid(value) } and map for a Validation is specified as: def map[B](f: A => B): Validation[B] = this match { case Valid(value) => Valid(f(value)) case Invalid(value) => Invalid(value) end And of course we can have a bit of syntactic sugar to not nest dozens of flatmaps, in Scala we use for: for { userId <- Valid("userId") address <- Invalid("address") } yield { User(id = userId, address = address) } Now in the main function we can handle the Valid and Invalid with pattern matching and look, we can stop the computation without throwing exceptions, which makes testing and everything way simpler.
- dschiptsov 11y agoWhile I really appreciate your cleverness, I would argue that this is an example of so-called overengineering or, an analogy from architecture - redundant decorations. Why do I need all this complications instead of a predicate? OK, in some statically typed languages which perform type-inference, there is a restrictions for homogeneity of conditionals and aggregates, otherwise all your type inference falls apart. So, to address this problem we could use data simple structures, like tuples, or we could create a new data-type. The canonical example that Nothing or Just T type. Because different branches of a conditional must be of the same type - this is the type. Also for the sake of type-consistency, it is parametrized type. In old-school languages we will just return nil, or (values ....) Semantically there is no difference. Ideally, types and semantics should not interfere. Complicating semantics in order to satisfy a type-system is a controversial idea. As for Monads - it is just an Abstract Data Type, nothing special, in which Semantics and Type information complement each other. It has been created to keep types consistent - a parameteized type along with two procedures. As an old-school programmer, I used to think about types as tags, the way it is in Lisps. (Of course, I know that these tags could be arranged in categories, hierarchies, and so-called "laws" could be defined). So, in my view, this is nothing but nested type-tags. It makes it easy to view Semantics and Types separately. In Haskell a Monad has another "function", which is, in my opinion, is the reason why it was created. Along with satisfying the type-system, it also ensures an order of evaluation in a lazy language. The semantics is obvious - you evaluate an expression, and lift (type-tag) the value back into Monad, so the whole expression has type Monad T. OK, this parametrized type is justified in Haskell, but in other languages, in my opinion, it is a redundant decoration, not a necessity.
- lk145 11y agoYeah, if we're talking about JavaScript, the explanation should start with promises. It's a pattern lots of people know, and it's a very real-world example. No need to front-load the explanation with a bunch of complication before people even know why they should care. I suspect some people (not saying this author) butcher the explanation intentionally. Makes it seem like an ineffable topic that only the smartest programmer can understand.
- yoz-y 11y agoEvery time I see one of the monad articles I think "this looks like promises, except it has a nicer syntax in Haskell". Is there any reason to use monads in (let's say) Javascript rather than promises? Are there any performance gains?
- skwosh 11y ago> Is there any reason to use monads in (let's say) Javascript rather than promises? Not intrinsically, IMO. The main value of monads is the shared interface, and its utility is contingent on tools that recognise that interface. In that respect, it's as useful as providing e.g. a `map` method for promises, arrays, dictionaries, etc. Though in my experience JavaScript rarely seems to be written with this kind of generic interface in mind (e.g. there's no coherent interface mandated between classes in the standard library).
- tel 11y agoSure. A little while ago there was an article (https://news.ycombinator.com/item?id=9907435 https://news.ycombinator.com/item?id=9907435) which suggested code like if (cond1) return val1; if (cond2) return val2; if (cond3) return val3; return defaultVal; is simpler and better than if (cond1) { return val1; } else if (cond2) { return val2; } else if (cond3) { return val3; } else { return defaultVal; } or something like that. In a certain sense this is objectively false as the second one uses fewer features than the first (a statement we can make formal using monads, but that's unrelated). I'd argue this is doubly true because whenever non-linear flow control begins to be used pervasively it is hard to know what code will be executed and under what conditions. With nesting at least these conditions are obvious. In any case, we can solve this conundrum through the existence of an Option or Maybe type which encapsulates the imperative behavior of short-circuiting failure directly and limits it's scope.