6 ms·
Much kerfuffling comes up about "What monads are" and "Are monads just pipes" and "is X or Y a monad." I'd like to volunteer to answer questions and provide exa
by KirinDave 9y ago
Much kerfuffling comes up about "What monads are" and "Are monads just pipes" and "is X or Y a monad." I'd like to volunteer to answer questions and provide examples people may have. This paper is a really practical reference for how to use them, but without an insight into what "them" is, it may not be helpful.
So feel free to fire away questions. I can provide simplified examples in a variety of examples upon request.
- winter_blue 9y agoHow would you explain monads to an OCaml programmer?
- KirinDave 9y agoI think maybe for Ocaml programmers, most probably understand the "what". They're functions holding to specific rules, dispatched on return types. That's pretty boring fare for Ocaml folks. Maybe the better question for Ocaml folks is "Why Monads?" Would that be a more interesting response?
- jasim 9y agoYes, it would be, please fire away. As an OCaml beginner my understanding is that I can do away with needing to understand monadic IO (yet) because OCaml is non-lazy and allows side-effects. I also like monadic parser combinators because they'll allow me to write parsers without the tedium of recursive descent, or being reliant upon parser generators. I have a rough understanding of monads as a purely algebraic abstraction (https://arcanesentiment.blogspot.in/2017/06/purely-algebraic-abstractions.html https://arcanesentiment.blogspot.in/2017/06/purely-algebraic...) and an even vaguer idea as a "representation of computations as values", based on this excellent thread: https://discuss.ocaml.org/t/can-monads-help-me-my-refactor-code-for-an-enhanced-data-structure/1064/5 https://discuss.ocaml.org/t/can-monads-help-me-my-refactor-c... But I don't grok it yet, which I'm sure I'll be able to once I find an occasion to use it. I suspect I'll hit that point sooner if I were using Haskell than OCaml though. I'm still collecting analogies (burritos, boxes, "smart pipes") and I want more. If nothing else, it is fun, and contrary to popular opinion, I don't think it distracts me from its essentially abstract nature.
- Drup 9y agoIf you want to grok monads as an OCaml programmer, there is a very simple solution: do some concurrent/system programming. Both Lwt and Async are monadic, but they are presented without requiring you to know about monads, so you can get to use them pretty easily, and after you've used them to build reasonably sized programs, then you'll have a good intuition of what's happening.
- KirinDave 9y agoSorry I didn't get back right away, but I did answer this to someone else in a more generic context. https://news.ycombinator.com/item?id=17008877 https://news.ycombinator.com/item?id=17008877 Ocaml folks have an awesome async library that is monadic, and I think maybe for OCaml specifically and Ocaml-specific value propositions, playing with that is better than any wall of text I can throw at you. But: parameterizing on a generic monad is what really is the superpower they give you.
- toolslive 9y agoSince OCaml allows side-effects, they are not really necessary to do simple things like printing some output. The standard library is not monadic in nature. However, there are a some very popular monadic libraries (for example, for concurrency you have lwt and async). The main advantage is that you have explicit information over which calls are blocking (it's in the signature), and explicit control over when you relinquish control back to the coroutine/fiber/lightweight_thread/future/whatever_you_want_to_call_it scheduler. Anyway, monads are hard to compose, so the shiny new thing here is called "algebraic effects". http://www.eff-lang.org/ http://www.eff-lang.org/
- tathougies 9y ago> Anyway, monads are hard to compose, Monads don't compose because monads shouldn't compose. Many things form monads that do not form proper effects. Perhaps the most obvious is the typical continuation monad. It is a good thing that this particular monad does not compose because if it did, you'd be in for a ton of hurt in understanding what happened when. Effect systems are good when all you want to do is write code that has effects. Monads are not really about effects, although they can be used to describe certain classes of them. Monads are nevertheless, strictly more powerful.
- catern 9y ago>Effect systems are good when all you want to do is write code that has effects. Monads are not really about effects, although they can be used to describe certain classes of them. Monads are nevertheless, strictly more powerful. This is not true. Monads and effect systems are equally powerful. More concretely: Effect systems are essentially delimited continuations. Monads and multi-shot delimited continuations are equally powerful, though some effect systems don't support invoking the continuation multiple times and so aren't multi-shot and therefore are less powerful than monads. You can express any monad with delimited continuations, and vice versa. And using some monad in do-notation, and using the corresponding delimited continuation implementation in normal (non-Haskell) sequential code, will look identical.
- 9y ago
- clementi128 9y agoMy understanding of monads, based on the examples in this paper, is that a monad essentially takes a series of computational steps and augments then with a secondary piece of data (a value to be printed, an error, etc). So my question is this: in languages which use monads (like Haskell), is this how monads are actually implemented? Does, say, printing values in Haskell actually involve keeping track of a lazy stream of output text, or does GHC just print out values the way an imperative language would, and use the concept of monads as a way to satisfy the type-system requirements?
- lobster_johnson 9y agoIt's my understanding that the IO monad is largely a type system thing. The IO monad doesn't "do" anything -- a call to the print function actually does write to a file descriptor right there and then. In a lazy, pure language like Haskell, there's the problem that since a print function doesn't actually compute anything, it doesn't return anything that makes sense functionally; it's not a function in the mathematical sense. In Haskell, a function is only evaluated when it needs to be (i.e. potentially out of order), and functions called with the same arguments can be memoized, and functions that don't return anything can conceptually be eliminated entirely. So the challenge is to somehow tag the print function as being unmemoizable. To do this, as far as I recall (it's been a while), the GHC's implementation of IO says that the monad wraps a value of a type "RealWorld". Every time you bind/return an IO function, you are sending the current RealWorld and then getting a new RealWorld back. This is hidden from the caller since it's a detail of the monad that you don't need to see. As far as I know, the world value is just a dummy value that is optimized away and doesn't exist at run time. But by forcing every IO to depend on the previous result, the "computations" will be chained in the right order. Edit: Haskell wiki has more: https://wiki.haskell.org/IO_inside https://wiki.haskell.org/IO_inside
- zenhack 9y agoI think you're right in that ghc's actual implementation is something like that, but it is a bit lacking in describing the semantics. To see why, imagine functions: getLine :: RealWorld -> (String, RealWorld) putLine :: RealWorld -> String -> RealWorld And some code like: Let (line, w1) = getLine w0 w2 = putLine w1 line w3 = putLine w2 "other stuff" w4 = putLine w3 line ... Now, imagine an optimization compiler for some reason decides it would be better to recompute `line`, rather than hang on to the value in the interim. It would be well within its rights to do so, because referential transparency guarantees that this is safe. IIRC ghc essentially "plays dumb" and just never duplicates a computation (which is not typically a desirable optimization at this level, though register allocators will sometimes do it). But it's arguably a hack. A better metaphor is to think of values of type IO a as fragments of a script in some other (imperative) language. The Haskell program merely computes the script, and the runtime then takes care of actually executing it. In this sense, effects in Haskell are not "side" -- they are first class citizens. What order the fragments of the imperative program are computed in (or how many times) is independent of the text of the final program. In very early versions of Haskell, main had a type roughly like [Response] -> [Request]; you computed a list of things for the runtime to do, as a function of the results of its (hopefully) previous commands. This kinda works, but it's easy to deadlock by creating a circular data dependency. What the Monad structure does for you is avoid that problem, by making it impossible to depend on a future value. It's also worth pointing out that all of this is specific to IO; understanding monads in general are neither necessary nor sufficient to understand Haskell's IO.
- dustingetz 9y agoI once saw a paper proposing a generic system connector type for composing systems-of-systems (it was much more sophisticated than just I/O) - I can no longer find the paper, do you know it or what is the state of the art here?
- KirinDave 9y agoI'm afraid I don't know, sorry! Sounds awesome. That said, similar concepts in a world where Monads exist include: Free monads, Freer monads, Final tagless encoding.
- CMCDragonkai 9y agoOperads
- nickpsecurity 9y agoBased on CMCDragonkai's comment, I'm guessing the one you're looking for is below. I thank both of you since it was an interesting concept and paper. Hopefully, the one you were looking for. http://categoricaldata.net/operadics/OperadicSoS.pdf http://categoricaldata.net/operadics/OperadicSoS.pdf
- CMCDragonkai 8y agoI tried to understand operads before. But I got sidetracked by graphical linear algebra: https://graphicallinearalgebra.net/ https://graphicallinearalgebra.net/
- theoh 9y agoIt sounds like you are literally offering to give a monad tutorial, which is brave because, notoriously, so many people have attempted that, usually using metaphors. Wadler's misleadingly titled earlier paper "The Essence of Functional Programming" is actually more elementary than the one linked to, and gets to the core of the issue: "2.1 What is a monad? For our purposes, a monad is a triple (M,unitM,bindM) consisting of a type constructor M and a pair of polymorphic functions. unitM :: a -> M a bindM :: M a -> (a -> M b) -> M b" If you can read this code and understand what a type constructor is, then that is the most general, least metaphorical way to understand monads in FP.
- KirinDave 9y agoI think maybe I am, but I have some tricks up my sleeve.
- chadcmulligan 9y agoSomething I've always wondered, you may be able to help with - why are monads? I.e. what is the problem they try to solve? I use objects and first class functions and they're pretty useful, occasionally even do a bit of currying (mainly because I think - ooh I can do a bit of currying here :-)), so how would Monads make my programs better/ easier etc. I've heard they can sequence your IO operations but in non functional languages thats not a problem. I feel there must be something they do that I'm missing so any pointers would be appreciated. Thanks. Edit: like the two things I spend most of my day writing code for are UI stuff and database code - can monads do anything for me there?
- KirinDave 9y agoSure. This is probably a much more useful question than "what". For Haskell, Purescript, Agda, Idris and other languages that like to rely on them, Monads represent some cool properties that make code extremely reusable. 1. Monads have a uniform, universal description of sequenced code. They can model essentially any form of computation that depends on its input and known state. This makes them a very nice level of abstraction to write highly generic tools. As an example I wrote about here awhile ago (https://news.ycombinator.com/item?id=16831985 https://news.ycombinator.com/item?id=16831985), it makes it easy to write async code execution strategies without ever even considering any aspect of the code other than that it's sequenced. 2. Monads describe behaviors, but a subtle point is that in a language with generic code (paramaterized or higher-kinded types, etc.) Monads let you write code to a GENERIC monad. As an example, what does this code do? data SomeThing = SomeThing { name :: Text, age :: Int } makeSomeThing :: (Monad m) => m Text -> m Int -> m Something makeSomeThing nameE ageE = do name <- nameE age <- ageE return $ SomeThing name age This code Makes SomeThing (and yes, I could write this more neatly as `liftM2 SomeThng`, and often we use Generics to mechancially derive this code, but I'm making a point here). But we haven't discussed anything except that: a. We are given effects to get name and age. b. We seqentially use those effects to produce two values. c. We then have an effect to produce SomeThing. That's pretty amazing, if you think about it. Depending on what the monad is, we could be calling over the network, or executing things in parallel, or parsing it off of stdin. We could use the Maybe monad to represent values that may be missing, or we might use Envy (https://hackage.haskell.org/package/envy https://hackage.haskell.org/package/envy) to read it from the environment. Monads give you a way to parameterize the structure of the logic you need to describe a task and make statements within that logic. This is why monadic programming tends to get really addictive. And as time has gone on, everyone has found new compiler techniques to make this kind of code easier AND more powerful. The next generation of programming languages folks are researching focus on Dependent Types (hi Agda and Idris) and Linear types (hi Rust and Pony and Idris), which let us take monads even further, allowing for a princpled approach even with previously complex tricks like interleaved effects, sophisticated type indexes, and consistent resource management. > Edit: like the two things I spend most of my day writing code for are UI stuff and database code - can monads do anything for me there? I just posted this code elsewhere, I just wrote a for-production Haskell microservice that I made generic over the database actions (type "p" in the code). This code does not know what database OR header validation scheme it's going to use, only that we've explained how to make CanValidate and Patchable actions in our action monad. This is better than writing for DI or mocking, DI and mocking usually couple your tests and underlying code structure deeply. This is Faking, where I have a trivial alternative service stubbed in so I can test my API logic without appealing to a database, and then run integration tests on my database actions separately. https://gist.github.com/KirinDave/9b4380376f9f748bcf0f8440decae517 https://gist.github.com/KirinDave/9b4380376f9f748bcf0f8440de... You might also see that I have "ValidAction" and "BaseAction" and there is a "validationHook." The framework I chose here lets me use "hooks". These hooks help the compiler check that I only run my database actions inside a Validated context, and don't try to validate again once I've already Validated. More precisely, they're helper functions to let us pretend indexed monads are normal monads. Here are sample implementations of CanValidate and Patchable from unit tests from that code so you can see the constraints in play. https://gist.github.com/KirinDave/fe317bffb5e4b2f1316c207325be2d1d https://gist.github.com/KirinDave/fe317bffb5e4b2f1316c207325...