In essence, the idea is that you are led through the process of implementing: early exit, nondeterminism, threaded state and coroutines using call/cc in scheme. Then monads are introduced as a way of typing those patterns.
In essence, the idea is that you are led through the process of implementing: early exit, nondeterminism, threaded state and coroutines using call/cc in scheme. Then monads are introduced as a way of typing those patterns.
I suspected this for a long time, but recently I saw a talk from Gilad Bracha - http://www.infoq.com/presentations/functional-pros-cons - which convinced me that I was right. Scroll to ~22 minutes into the talk for relevant section.
Ok, I finally said it. I feel better now, thanks ;)
I suspect it may be painful in some cases, it's somewhat like a clash of different cultures. The guy is firmly rooted in Smalltalk and Lisp schools of thinking and everything he says is rather obvious for those who share the same background. On the other hand it may be obviously false for people following ML tradition and even more so for recent converts.
Watching this talk calmed me down. It turns out that I'm not an idiot, that I know how to do FP (because I quite like it!) and that there's no need to change my ways just because some vocal minority proclaims me a heretic for preferring meaningful names or no-nonsense IO handling. On the whole I think it was an hour well-spent. YMMV ofc.
E.g.
x <- foo 2 -- x = foo(2)
y <- bar 4 -- y = bar(4)
return (x+y) -- return (x+y)
But something that's always missing in such discussions is that haskell allows you to define a_better_word = return
and then you can freely forget about 'return' in your own code, except when you roll your own monad instances, which is rarer than a blue moon.https://www.haskell.org/haskellwiki/Functor-Applicative-Mona...
func1 :: Maybe Int
func1 = do x <- foo
y <- bar
return (x + y) -- Using return
func2 :: Maybe Int
func2 = do x <- foo
y <- bar
Just (x + y) -- Building a monadic value explicitly
func3 :: Maybe Int
func3 = do x <- foo
y <- bar
plusJust x y -- Using some other function
plusJust :: Int -> Int -> Maybe Int
plusJust x y = Just (x + y)
Also, since `return` is a function, we can use it outside do-notation: wrapAndApply :: a -> (a -> b) -> [b]
wrapAndApply x f = fmap f (return x)
What makes `return` "special" is that it's a method of the `Monad` typeclass. In other words, the name `return` is overloaded to work with any instance of `Monad` (`IO`, `Maybe`, `List`, etc.), depending on the type that's required of it.Loads of other functions are overloaded like this, eg. `+` has implementations for `Int`, `Float`, etc. so it's not monad-specific.
The part which is monad-specific is that Haskell's do-notation is hard-coded to the built-in Monad typeclass. We're completely free to make our own monad implementation, separate to Haskell's built-in one, but we won't be able to use do-notation with them unless we implement the built-in `Monad` typeclass as well. That could be as simple as mapping one name to the other:
instance MyMonad a => Monad a
return = myReturn
(>>=) = myBindThat's not quite true, at least in GHC. It's hard-coded to use (>>=) and fail, but the only thing that prevents you from defining your own is the name collision. With -XNoImplicitPrelude, you can (and they can even be locally scoped - I did some cute things with this once that I'd never want to see in production code...).
Aside: You realize that the word "monad" (and "dyad") already has a very specific meaning and that you assaulted that meaning and now are trying to beat the poor word into submission? If I had to choose I'd say that J programmers have much more of a right to use and define this word: a single-argument function ("mono") makes more sense to be called a "monad" than an instance of "flat-mappable" interface.
> since its part of the actual syntax of Haskell do-notation
It's not, which makes using it even dumber - as it's no problem at all to change it.
F# uses monads extensively, as one of the selling points of the language, yet the m-word rarely if ever is mentioned in the docs. That's because designers of that language were rational enough to realize that "computation expression" is a name that both conveys some basic intuition about the thing in question and is concise enough to allow talking about the thing abstractly. From my perspective I don't see any, at all, arguments for persistent use of the M-word by the community of some programming language. Other than trying to be different, or something.
Believe it or not, names do matter. Good name speeds up learning/understanding considerably; a bad name slows it down. It's sometimes - rarely! - worth it to invent completely new name. That's when the thing you want to name really is dissimilar to anything else and when re-using some name would introduce false intuitions about the object. But monads ARE NOT SUCH THINGS (sorry for shouting). "You could have invented monads", and quite possibly you did a few times already, and that probably wouldn't be the case if the idea was that ground-breaking, that unprecedented.
Ok, nevermind - I'm getting angry for no reason, I should stop it already.
They have meaning in the same sense that any words have meaning -- that is, they communicate meaning between an existing group of people.
> And sometimes, which is worse, the meaning you want to give them is completely unrelated or even opposed to everything they are or do.
Like many words, their meaning in a specific technical context differs from their meaning in other contexts. This is not particularly unusual.
> Aside: You realize that the word "monad" (and "dyad") already has a very specific meaning
"Monad" has several distinct well-established meanings in different domains besides its use is Category Theory (and thus Haskell). So what?
> and that you assaulted that meaning and now are trying to beat the poor word into submission?
The meaning of "monad" in Haskell comes directly from its meaning in Category Theory, which is inspired by (though not the same as) its earlier use in mathematics stretching back to its use in metaphysics (particularly, through Leibnitz's Monadology), which has its roots in its use in Classical philosophy, from whence also comes all its other uses.
> If I had to choose I'd say that J programmers have much more of a right to use and define this word
Well, you don't have to or even get to choose that; words can and do have uses in different contexts and you don't get to choose one and make it the exclusive use of the word because you like it more.
> a single-argument function ("mono") makes more sense to be called a "monad" than an instance of "flat-mappable" interface.
Not really. Sure, a single argument function might have a better claim to the title "mono-argument function" but "-ad" isn't generally a suffix that means "-argument function".
> words can and do have uses in different contexts and you don't get to choose one and make it the exclusive use of the word because you like it more.
it's just that I think the same applies to concepts. You shouldn't be able to "own" the concept and to insist that it should be called as you want in every context. I see comments and posts about how "X or Y is just a monad!" to be exactly this: people trying to "own" a concept.
Just as both musicians, mathematicians and programmers can all use a single word for different things, different programmers should be able to use the same concept without using the same word for it.
In short (directed to random commenters on the Internet, no to you personally): stop correcting me when I say "computation expression builder", don't say that what I "really mean" is a monad. It's not, what I "really mean" is an abstract concept and we both know its properties, and you have no right to oppress me because I chose different word for it.
I do prefer "flatMap" to "bind", as practiced in say Scala, because it's a direct reference to this property: "m.flatMap(f) == m.map(f).flatten"
Don't know the equivalent of flatten in Haskell, but if you want a signature then it is something like:
def flatten(m: M[M[A]]): M[A]
Or another way of thinking about it ... flatten(m) == flatMap(m)(x => x)
So basically, in order to define a monad, implementing flatMap is equivalent to implementing flatten. And I think that "flatten" as a verb is very intuitive, although intuitiveness is subjective since it's tightly related to familiarity - but flatten has been used in many languages as an operation available for collections and it works for monads in general just as well.The Haskell equivalent is join :: Monad m => m (m a) -> m a
We also have the equivalent specialized to lists: concat :: [[a]] -> [a]
And as you say, join = bind id = (>>= id).
Incidentally, in math that function is the "eta" in the triple representing a monad.
I suspect that one could get through the entire thing, handily grasp all of it, and be ready to apply what was learned in practice without ever realizing this had anything to do with the dreaded M-word.
On a somewhat related note, I'm impressed by how Microsoft has their entire developer community comfortably using a few basic monads without even realizing it. I've even had some success with hijacking it. The terms they chose are explicitly coupled to the idea of querying, but I've been able to produce some implementations of .NET's version of the pattern that still produce good and readable results when you use the LINQ query syntax with them. I wouldn't say it's a perfect renaming of all the concepts, but if the goal is to make the concept intuitive enough for most users to tackle without fears then damn if it isn't an impressive 90% solution.
Eventually, the reader learns about monads without ever explicitly learning about monads.
My broader disagreement is that these names do mean something - they mean the same thing they do in math. Using the same name makes it easier for me to find more relevant things, and it can be tremendously useful to be able pull over results and intuitions from other contexts. There's no good reason to demand an additional translation step when we're talking about the same things.
He does seem to have a reasonable grasp of the what, but is shaky enough on the why that he's presented a few straw men.
Regarding Hindly-Milner type inference, I believe that there have ever been advocates that have gone from "you don't need to specify a type anywhere" to "you shouldn't specify a type anywhere". That is not how practitioners use these languages, these days. That you need redundancy for error correction (an accurate and important point) does not mean that your particular level of redundancy is best. Inference allows you to specify types where they will make things clearer, and lets the compiler fill in the gaps. It also lets you ask questions of your interpreter about what shape of thing will fit in a given hole. Even before GHC added TypedHoles, you could open GHCi and say :t (\ x -> some expression involving x) and get back the type of x as the first argument.
There's not enough room in this margin to fit the rest...
Would it be better to use Python or Javascript which more people understand but still has that functional flair? Just an idea. Either way I look forward to reading it!
That's probably survivor-bias (amongst Haskellers, at least); ie. those who haven't grasped it yet probably won't class themselves as Haskellers.
> Would it be better to use Python or Javascript which more people understand but still has that functional flair?
The problem with using such languages is that they're always dealing with concrete values; in particular they have no concept of interfaces (like Java) or typeclasses (like Haskell). It's certainly possible to use monads in such languages, but it's often hard to justify their use, since there are always less-complex ways to implement particular examples, and without interfaces it's hard to relate the examples together.
For example, here are a bunch of monad implementations ("return" and "bind", AKA ">>=") in Javascript, but it's not at all obvious why they are related to each other:
function state_return(val) {
return [null, val];
}
function state_bind(x, f) {
return f(x[0], x[1]);
}
function maybe_return(val) {
return [val];
}
function maybe_bind(x, f) {
return (x.length > 0)? f(x[0])
: [];
}
function list_return(val) {
return [val];
}
function list_bind(x, f) {
return [].concat.apply([], x.map(f));
}
function read_return(val) {
return function(x) { return val; };
}
function read_bind(x, f) {
return function(arg) {
f(x(arg))(arg);
};
}
The problem is that in Javascript, the implementation details are there for all to see. I might want to give you a string which depends on some integer state, but you can always see that it's actually implemented as an array containing an int and a string. You're also free to mess up that array by adding or removing elements from it, which are perfectly valid array operations, but which makes no sense for "stateful values".Interfaces like monads are useful when we're not free to mess around with the implementation; when we must perform all actions through a limited API. When we're dealing with such concerns every day ("I'd like to allow X, but don't want anyone to abuse it and end up with Y") then monads (and functors and applicatives, which they're based on) are really useful as standard APIs for allowing arbitrary operations to be performed, under the control of the API.
The only example above that even comes close to this idea is the "reader" monad, implemented with `read_return` and `read_bind`. This is because values in the reader monad lets us manipulate the return values of functions, and even in dynamic languages a first-class function is pretty much a black box, so there's little we can do to mess it up.
https://blog.jcoglan.com/2011/03/05/translation-from-haskell...
http://stackoverflow.com/questions/11871065/monads-in-javasc...