5 ms·
I think most monad tutorials would be quite ok if they didn't use words like "monad", "bind", "return" and so on. It's just stupid to insist on using names whic
by klibertp 12y ago
I think most monad tutorials would be quite ok if they didn't use words like "monad", "bind", "return" and so on. It's just stupid to insist on using names which mean nothing or when they mean something (return) it's actually opposite of what they're supposed to do. And no, there are no upsides to this: you math-inclined people may think so because you were trained to think that way, but it's simply not true.
I suspected this for a long time, but recently I saw a talk from Gilad Bracha - http://www.infoq.com/presentations/functional-pros-cons 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 ;)
- deleted 12y ago[deleted]
- abathologist 12y agoI find that talk really painful. I do not recommend it. It is mean spirited, full of misleading characterizations, and - I think - it misses the core point of functional programming. Gary Verhaegen's comment "this was a waste of time" addresses the many problems effectively. Brian Craft's "ugh" addresses others. Either of those comments are more worthwhile than the video itself.
- klibertp 12y ago> I find that talk really painful. 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.
- abathologist 12y agoThat makes sense. Your points are well taken. Also, in retrospect, I realize that my reply wasn't helpful for the discussion at hand.
- ky3 12y agoYes, 'return' is egregious. I believe it was co-designed with haskell's do-notation. So that you could write purely declarative code that was a spitting image of the imperative equivalent. 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.
- dllthomas 12y agoWith AMP, it should always be possible to use pure in place of return. https://www.haskell.org/haskellwiki/Functor-Applicative-Monad_Proposal https://www.haskell.org/haskellwiki/Functor-Applicative-Mona...
- dragonwriter 12y agoNone of those names "mean nothing"; they all have very specific meanings. Its true that in the case of return, the meaning in the context of Haskell monads is somewhat special, but since its part of the actual syntax of Haskell do-notation, its particularly important to address it in Haskell-oriented monad tutorials, whether or not it was the best choice from a syntax design perspective.
- chriswarbo 12y agoYou may already be aware of this, but for those who aren't: `return` isn't actually part of do-notation syntax, it's a function name. It's true that do-notation must result in a monadic value, but that's just because of the type. `return` can be used to construct that monadic value, but there are many other ways to do so: 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 (>>=) = myBind
- dllthomas 12y ago"The part which is monad-specific is that Haskell's do-notation is hard-coded to the built-in Monad typeclass." That'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...).
- bad_user 12y agoI believe Monad is fine as a term, because you cannot describe it based on resemblance with real-world objects, as more conventional OOP design-patterns are doing. And I think that the name of a design pattern should be first of all unique and easy to remember and not necessarily descriptive, because the whole point of coming up with a design pattern is to grow the vocabulary of those using it and naming clashes are bad. 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"
- dllthomas 12y agoI don't know enough Scala... "flatten" seems to be equivalent to "join" from Haskell/math - is "flatten" specialized to lists? For that matter, is "map"?
- bad_user 12y agoWhat do you mean by specialized? 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.
- dllthomas 12y agoSpecialized in the sense of a more restricted type. From your answer, it seems like "no" - though my Scala is superficial and rusty. 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.
- bunderbunder 12y agoSee here: http://fsharpforfunandprofit.com/posts/recipe-part2/ http://fsharpforfunandprofit.com/posts/recipe-part2/ 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.
- saryant 12y agoI don't know if it was intentional, but most of the intro literature around Scala seems to avoid terms like monad, just introducing the reader to Option, Future, etc., and letting the reader notice the common patterns (map, flatMap). Eventually, the reader learns about monads without ever explicitly learning about monads.
- dllthomas 12y agoWhile I generally disagree with you here, I actually do agree about "return". Hopefully we can deprecate it in favor of pure now that all Monads must be Applicatives. 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.
- dllthomas 12y agoWatched the video. It was interesting. Gilad Bracha certainly knows his stuff. However, while he knows his stuff, his grasp of the stuff he's criticizing is less thorough. 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...