6 ms·
The whole point about monads is that they provide a uniform interface for working with values that carry some extra context. The genericity of the interface is
by throwaway283719 12y ago
The whole point about monads is that they provide a uniform interface for working with values that carry some extra context. The genericity of the interface is nice, in that it allows you to write generic monad combinators, but they are only useful because of the context (effects, control flow, etc). If you don't describe any of the implementations, you are missing the point!
The best introduction to monads was written in 2006 (more than eight years ago now) and I don't imagine that a better one will be written in the near future -
http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html http://blog.sigfpe.com/2006/08/you-could-have-invented-monad...
- elwell 12y agoIf you want video, this is probably worth watching; haven't finished it yet. https://www.youtube.com/watch?v=ZhuHCtR3xq8 https://www.youtube.com/watch?v=ZhuHCtR3xq8
- nightski 12y agoAre you sure? There are plenty useful monads which are not effects... In fact the article you linked even says so.
- throwaway283719 12y agoYeah, I should have written "computational context" not "effects".
- Patient0 12y agoOne thing that this article and lot of other Monad articles gloss over is the scoping in "do" notation and when using the >>= operator. The example: return 7 >>= (\x -> Writer (x+1,"inc.")) >>= (\x -> Writer (2*x,"double.")) >>= (\x -> Writer (x-1,"dec.")) works for this example, but a more accurate representation of how "do" notation is expanded is: return 7 >>= (\x -> Writer (x+1,"inc.") >>= (\x -> Writer (2*x,"double.") >>= (\x -> Writer (x-1,"dec.")))) Note that I've moved the parentheses. Actually, you can, equivalently, omit the parentheses for the same meaning, because the '->' has very low precedence: return 7 >>= \x -> Writer (x+1,"inc.") >>= \x -> Writer (2*x,"double.") >>= \x -> Writer (x-1,"dec.") The point is, most times each of subsequent functions need to "see" what was in scope in the earlier functions. Most times, the later monadic actions need information that is pulled out of the monad in previous actions. For example, consider the following: main = putStrLn "Enter your first name" >>= \_ -> getLine >>= \first -> putStrLn "Enter your last name" >>= \_ -> getLine >>= \last -> putStrLn ("Hello " ++ first ++ " " ++ last ++ ".") The last monadic action needs both "first" and "last" to both be in scope. This only works because of the way that it is implicitly parenthesized. Until I "got" this, I was very confused by most of the Monad examples I found. Here's an article I wrote about the IO monad which also makes this point: https://github.com/Patient0/IOMonad https://github.com/Patient0/IOMonad
- jerf 12y agoI wrote this up, "So You Want To Write A Monad Tutorial in Not-Haskell": http://www.jerf.org/iri/post/2928 http://www.jerf.org/iri/post/2928 It focuses on all the things I've seen people miss about "monads" when they try to write an implementation in another language after the third time I saw someone claim they'd implemented "monads" in Javascript using method chaining, which I do not believe to be possible in part because of the issue you point out, which is that the scope builds up as you go. It is perfectly reasonable to also read this as the list of elements of monadery that even tutorials written in Haskell tend to miss or fail to explain.
- mattfenwick 12y agoFrom your article: > Monads are not "about effects". What is your definition of "effects"? I ask because this article[0] and many others[1] seem[2] to use a contradictory definition, in which failure, nondeterminism, state etc. are viewed as effects and monads are a means of implementing those effects. ------ [0]: http://www.sciencedirect.com/science/article/pii/S2352220814000194 http://www.sciencedirect.com/science/article/pii/S2352220814... [1]: http://scholar.google.com/scholar?q=algebraic+effects+monad&btnG=&hl=en&as_sdt=0%2C7 http://scholar.google.com/scholar?q=algebraic+effects+monad&... [2]: as far as I understand them; I may be wrong.
- tel 12y ago"Effect" is a more general idea. It's a lens for viewing any kind of impure computation[0]. Monads are a model for effects. You can view them as pure[1] computation in lambda calculus or as defining a region of code which, internally, has impure effects. So to summarize: effects are a general concept, monads are a particular technology for implementing that concept. Further complexity ensues when people start talking about the general concept of a monad which is interesting in its own right but it has a more sophisticated relationship with the concept of effects. [0] Purity is a property of, say, functions. Its definition is a function `f` is pure if and only if const () . f = const () which usually means that non-termination is impure as well. The notion of equality you use above can finesse this definition a lot. [1] As stated in [0], non-termination is an effect, so Haskell monads are impure in that sense. Haskell typically ignores non-termination effects, though. Generally, monads would work more or less just fine even without non-termination though. Externally you can think of them as pure.
- jfarmer 12y agoThe best introduction to monads that I've read is the original paper that discussed them in the context of functional programming: http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/ba...
- ufo 12y agoOn the same vein, SPJ's "Tacking the Awkward Squad" is also a great old paper, and focuses more on impure effects than Wadler's
- chriswarbo 12y agoIt's not even about the effects, really. It's simpler to think of Monad as an interface for manipulating encapsulated data in a way which keeps it encapsulated. The simplest way to encapsulate data is to wrap it using a constructor which we keep private to our module: module M1 (Encap(), val1, val2) data Encap a = mkEncap a val1 :: Encap Int val1 = mkEncap 5 val2 :: Encap Int val2 = mkEncap 10 Other modules importing M1 get access to the Encap type, val1 and val2 but not the mkEncap constructor. They can use val1 and val2 as-is, but they can't construct new Encap values or destruct existing Encap values to get at their contents. The problem is, there's not much we can do with this interface. One way we can make this more useful is being able to apply some function to an encapsulated value. That's what Functor is for, so we can add this to M1: instance Functor Encap where fmap f (mkEncap x) = mkEncap (f x) Now users of M1 can transform encapsulated values without being able to break the encapsulation. For example: val1Plus7 :: Encap Int val1Plus7 = fmap (+7) val1 val2Str :: Encap String val2Str = fmap show val2 We're still pretty limited though, since there's no way to combine encapsulated values into new encapsulated values, or to encapsulate our own values. That's what Applicative provides, by letting us construct encapsulated values without gaining the ability to destruct them, and by allowing encapsulated functions to be applied to encapsulated values (since functions are closures, this lets us gather up encapsulated values and combine them arbitrarily): instance Applicative Encap where pure x = mkEncap x (mkEncap f) <*> (mkEncap x) = mkEncap (f x) Now users of M1 can encapsulate and combine values, like this: -- val1 + val2 val1PlusVal2 :: Encap Int val1PlusVal2 = fmap (+) val1 <*> val2 -- New encapsulated string val3 :: Encap String val3 = pure "Hello world" -- val3 repeated val1 times val3Repeated :: Encap String val3Repeated = fmap rep val1 <*> val3 where rep n _ | n <= 0 = "" rep n s = s ++ rep (n-1) s This is quite a powerful interface, but one thing we can't do is `collapse` double-encapsulated values into single-encapsulated values. That's what Monad provides: join :: Encap (Encap a) -> Encap a join (mkEncap (mkEncap x)) = mkEncap x An alternative, but equivalent, definition is to allow calls to encapsulation-producing functions without encapsulating their result. Haskell's Monad is defined this way: instance Monad Encap where (>>=) :: Encap a -> (a -> Encap b) -> Encap b (mkEncap x) >>= f = f x These kind of encapsulated values turns out to hold effects without breaking the language, and these interfaces turn out to be powerful enough for general computation.
- pedrow 12y agoFor those like me who find it hard to think outside the imperative programming box, Eric Lippert's series of articles is good. http://ericlippert.com/2013/02/21/monads-part-one/ http://ericlippert.com/2013/02/21/monads-part-one/ A lot of Monad articles like to explain 'what' without first establishing 'why' (IMHO)