5 ms·
I seem to be monad proof - cannot understand them from any explanation whatsoever. If I'm not alone then it's going to be hard to hire people that get it - on t
by t43562 5y ago
I seem to be monad proof - cannot understand them from any explanation whatsoever. If I'm not alone then it's going to be hard to hire people that get it - on top of how hard it is to hire skilled people at all.
- askonomm 5y agoI, too, have tried to figure out what exactly are monads, and have come away even more confused than I was before.
- tester756 5y agoI've heard that dividing e.g 1 by 0 in Excel feels like a monad it's either value or dividing by 0 error :)
- exdsq 5y agoYou know how some functions return a value or an error? Imagine trying to compose several of those functions, passing the value into the next function or handling it’s error. That ends up with a lot of boilerplate. The first time I wrote a Monad was because of this issue. Wrapping this series of function calls in a Monad allows you to write something that reduces that boilerplate by handling the errors of any functions within it, so you can compose them like you’d want How do you handle state in a language like Haskell that doesn’t have mutable state? You pass a data value between functions keeping track of everything, as an extra argument. Imagine the boilerplate of that! Every function suddenly needs an extra argument for Data, regardless of if it needs it, to keep it around. The State Monad deals with that boilerplate.
- benreesman 5y agoThis an analogy, not a definition. But imagine a pointer to a struct that could only be dereferenced by passing it to another function.
- triska 5y agoPersonally, I think monads are hard to understand in Haskell mostly due to its quite complex and comparatively ad hoc type system. Let's remove all the complexities that are introduced by the static type system for a moment, and focus on the essence. To illustrate the key point, let's use a dynamically typed logic programming language such as Prolog, where we think in terms of relations instead of functions. Suppose we have a predicate definition of this shape: pred(S0, S) :- goal_1(S0, S1), goal_2(S1, S2), goal_3(S2, S3), ..., goal_N(SN1, S). In this case, the arguments clearly follow a pattern: They are "threaded through", starting from S0, and ending with S, in a sequence of the form S0 → S1 → S2 → ⋯ → S. This shape of argument passing is common, for example if you want to describe successive "modifications" of a data structure in the form of relations between an initial state S0 of the data, several intermediary states (S1, S2, ...), and a final state S. Or successive applications of side-effects: Think of sending packets over a network, or writing a sequence of strings to a file, where each such relation affects the state of a socket or other entity. Or when describing strings, where the string becomes successively "more known" in the form of further and further instantiation of a list of characters. You can think of monads as mechanism to make this cumbersome explicit passing of arguments implicit. For example, since the arguments that are passed around follow such a clear pattern, we could write the example above equivalently as: pred --> goal_1, goal_2, goal_3, ..., goal_N. and let the compiler do the necessary transformation for us. In fact, that's exactly what also happens in Prolog, using definite clause grammars (DCGs), which you can think of as a mechanism to implicitly describe such a passing of arguments, so that you do not have to write them yourself.
- throwaway17_17 5y agoCan you expand on what you mean by calling Haskell’s type system ‘ad hoc’? I was under the impression that ‘ad hoc’ was basically the opposite of formalized/well-founded. That doesn’t sound like an accurate description of Haskell’s type system in my understanding.
- triska 5y agoFor example, aptly named ad hoc polymorphism and overloading violates referential transparency: ( (7^7^7`mod`5`mod`2)==1, [False,True]!!(7^7^7`mod`5`mod`2) ) This yields: (True,False) suggesting that the same arithmetic expression is both 1 and 0. Yet, the ability to plug in expressions and reason algebraically about their values was frequently advertised as a core property of Haskell. This example shows that we cannot do this in general, we must also take into account the types, which in this case seem to be defined in a rather "ad hoc" way, by reflecting low-level implementation details such as the range of admissible integers in the type system. In GHC, there is a dedicated flag to spot such cases (-fwarn-type-defaults). In general, the guarentees that the type system actually gives and also the ways to specify them appear somewhat unfinished and are also quite hard to understand, often necessitating semantic restrictions and syntactic extensions. For further examples, see for instance: https://stackoverflow.com/questions/27019906/type-inference-interferes-with-referential-transparency https://stackoverflow.com/questions/27019906/type-inference-... https://stackoverflow.com/questions/14865734/referential-transparency-with-polymorphism-in-haskell https://stackoverflow.com/questions/14865734/referential-tra...
- worldsayshi 5y agoHave you spent a lot of time trying to write code using monads? If not, that's the only real way to learn I think. I wouldn't expect someone to understand what object oriented programming or functional programming is unless they've tried to solve quite a few problems using those techniques. Try solving a non trivial problem on Haskell. You may not fully understand monads but you should get a feeling for it.
- jim-jim-jim 5y agoDon't let people waste your time with metaphors. A monad is something that you can use `flatMap` on. That's it. It's not it, of course, but that's all you need to know. You can open up the js console in your browser right now and play around with `map` vs `flatMap` since lists are monads. It's not a very useful function on lists, but if you take pains to simulate an optional type with it, where an empty list is Nothing/failure and a list with a value indicates Just/success, you can see how it might be an exciting form of control flow: chaining together optimistic functions, but short circuiting if any of them fail. Happy path only programming. I wouldn't worry too much if this is yet another shit explanation. Monads will eventually be added in earnest to your language of choice, you will use them, and think "oh, why could nobody explain that?"
- monads 5y agoYes, I think metaphors are used to put the formal definition of computation monads into some concrete situations (for getting intuition). But "too many bad metaphors" will make monads something mysterious, incomprehensible.
- ParetoOptimal 5y agoHm, small addition: > A monad is something that you can use `flatMap` on that cannot act in a surprising way. That's it.
- klysm 5y agoThe easiest way for me to learn was by using limited versions of them in scala. I started to “get it” after really applying combinations of Future, Option, Try and friends.
- danuker 5y agoHere is an article and video on "Railway Oriented Programming": https://fsharpforfunandprofit.com/rop/ https://fsharpforfunandprofit.com/rop/ You won't believe it's about monads!
- parentheses 5y agoMonads are cross cutting functionality shaped into a unified interface. Kinds that are in common use: - lists are monads that provide multiplicity - options are monads that provide nullability - try wrappers are monads that provide failure semantics - futures and promises are monads that provide asyncrony - either is a monad that provides duality … and many more. The key concept here is that you wrap computations in some behavior before executing them. there are many problems that can be monadefied. The problem is that learning about monads comes with a lot of threads to pull about category theory and it’s use in computation. This is where I feel folks get lost. Monads are tremendously useful and understandable. Many go full theory when teaching them and that is regrettable.
- giomasce 5y agoIf you've already tried, you probably already came across this explanation, so there are good chances it won't help, but let me just try: I think a monad as a computation, meaning the act of computing something. A box with a button, that will do something (and possibly return a value) when you press the button. Before the button is pressed, you can't know what value is going to be generated, because none has yet. The "return" monad operation does a very simple thing: given a value, constructs a box that is going to generate that value once you press the button. Not very useful in itself, but the point is that that value can be used for further computation. The "bind" operation precisely takes another monad and a function, so now we have two monads: the first one is provided by you and the second one is the result of the "bind" operation. Think the first monad as being "embedded" in the second one. When you press the button on the outer monad, first the button on the inner monad is pressed and a value is generated; then the value is mapped through the function; and then the result of the function is the value generated by the outer monad. In practice you extended the inner monad, doing some more computation (the function) after it had executed. In the case of the Haskell IO monad, you can't really press the button inside your program: there is no way to extract values that were put in a IO monad. Still, on object of the IO monad encodes what you want the program to do. Basically the Haskell interpreter/compiler gives you the opportunity the press just once the button of a single IO monad, precisely of the one that is given by the main symbol (which has type "IO ()"). By composing monads in the right way, you can arrange your program to do what you want. In other words, a monad like IO, where you can't press the button yourself, allows you to express precisely what a program with side-effects should be: something for which you cannot know the result (you cannot press the button and see the generated value in the program) unless you really execute it and commit to the side effects (which is what happen when main returns something and the single allowed button press is executed on that thing).
- jauco 5y agoHave you ever tried: https://ericlippert.com/2013/02/21/monads-part-one/ https://ericlippert.com/2013/02/21/monads-part-one/ ? it was the one that did it for me.
- monads 5y agoThe definition of computation monads (in sense of Moggy) is simple, but not trivial. It's deep since it generalizes many computation "phenomena": side effects, exceptions, etc. Conceptually, a "pure" function is something as `A -> B`, so all information about its internal (e.g. side effects) lost: we put something of type A but we can only observe a result of type B. The idea of computation monads is to attach the result type of the function with a "constructor" T, then the type T B is used to "restore" (or to reason about) the lost information. In composing such functions, T X (for some result type X) is all information we need, then this explains naturally the third rule of the Kleisli triple `f: A -> T B; g: B -> T C`: A -> T C
- camgunz 5y agoI'll also get nerd-sniped here :) Monads are state/system backdoors for pure functions. Life would be pretty cool if we could just map/filter/reduce everything, it's a beautiful model and ripe for insane optimization. Sadly programs like that aren't useful, and programmers always want to do gross animal stuff like writing to sockets or files. Enter the humble monad, a way to pass something like a database connection, a logger, a system IO instance, or whatever into your cool nest of map calls. John Ousterhout talks about this problem in A Philosophy of Software Design. Sometimes you need to carry things around, like a rendering context, a browser window, whatever, and you're give a couple of imperfect options: - Make it global - Thread it through literally everything (React people might know #2 as "prop drilling", and they might know #1 as hooks) Monads are infrastructure to help you do #2, since #1, well, people don't like it.
- convolvatron 5y agoi'm with the gp. i guess another way i think about it (which might be wrong, please tell me) is that with monads we can talk treat something like pipe as a pure object, even though whats its carrying is decidely not
- camgunz 5y agoYup, exactly. It's basically like if you have a bunch of simple arithmetic functions: half(x) double(x) triple(x) you can easily do things like: double(half(triple(13))) Or a la pipes: triple 13 | half | double But now what if you want to print something in `half` without a global? Well, I guess it's half(x, printer) now. Hmm and now `triple` has to carry that around too, so it's triple(x, printer). Well, that makes me think everything gets a printer, so it's double(x, printer) now too. Well, printing is cool, but now we're a real company and real companies log everything. I... guess we're making new functions? half_log(x, logger) and triple_log(x, logger) and double_log(x, logger). And... new pipelines? Ugh. At this point you might be thinking one of a number of things: - Are globals really that bad? - Can OOP fix this (dependency injection, builders)? - Surely there's some kind of framework for this. And if you're a functional programmer who's super not into globals or OOP, you might start seeing the appeal of #3 there. Et voila, a whole bunch of functions that handle this "pass an extra blob into your pipelines" thing.
- amelius 5y agoTry this: https://news.ycombinator.com/item?id=7466351 https://news.ycombinator.com/item?id=7466351
- throwaway858 5y agoThe problem is that you are trying to understand them from an explanation, when the correct method is to code with them and feel them and get used to them. The programming world is filled with concepts that beginners find extremely difficult to comprehend from explanations: - pointers - closures - recursion Monads are no more difficult to grasp than any of these concepts (but no one would ever claim that it will be "hard to hire devs that understand closures...")
- t43562 5y agoI take your point but actually I don't think there is an abundance of developers who understand closures. :-)
- hota_mazi 5y agoYou know how in shells, you can use the pipe operator, "|", to chain commands together? Monads are the same thing, but for functions. They adjust the output of one function so it can become the input of the next one.
- Twisol 5y agoA structure is a monad if you can nest one of itself inside another without a visible seam. If you have a binary tree whose leaves are also binary trees, is that meaningfully different from a single grafted binary tree? Not really. If you have a sequence (list) of sequences, is that meaningfully different from having a single sequence? Not really. If you have an option of option of bool, is that meaningfully different from having a single optional? Not really. More precisely, if you don't care about the difference, structures that are monads let you smooth over the seam between nesting levels. The flatMap operator lets you extend a data structure at each of its leaves. That is, you can map over each leaf and replace it with another structure of the same kind, then smooth over the seam. The connection with computation comes about by imagining the data structure as the history of outputs of a process; a trace. You can extend a program trace by specifying what to do next (via flatMap). Clever choices of data structure give you interesting alternative models of computation. But, this is an application of modeling with monads. It's not what a monad is.
- vore 5y agoI think a lot of explanations miss the mark on what exactly a monad is, and make it out to be like some kind of tool or data structure or something, when it's really just describing operations on some piece of data. It can be helpful to just take the word "monad" out of your brain and work up from understanding the idea of a functor first, then gradually understand what a monad is. In the end, I think, monads are not that exciting outside the world of pure FP :) https://twitter.com/plt_borat/status/228009057670291456 https://twitter.com/plt_borat/status/228009057670291456
- chrisshroba 5y agoI felt the same way. This is the explanation that made it finally make sense for me [1]. Having a bunch of examples right in a row helped me see the general form of a monad (instead of once again having the "Maybe Monad" example thrown at me.)