What I Wish I Knew When Learning Haskell
dev.stephendiehl.com
dev.stephendiehl.com
Use monads for bailing out of computations early (Maybe/Either) or passing some state along through a computation (State). When you're comfortable with those concepts, look at ST for a generalization of what IO does. That is something you can actually use to simplify your own programs.
It's slightly disappointing, though, that State doesn't work that well when translated back to Lisp: http://marijnhaverbeke.nl/monad.html
On the other hand, I think avoiding do-syntax makes sense at first.
I know (and use) basically every other CS concept. Monads, though, I've never bothered looking into, because it seems like everyone who talks about them can't resist the urge to use flowery descriptions of their possibilities, rather than examples of their pragmatism.
Also, it's suspicious that pg has never once mentioned monads: https://www.google.com/search?q=site%3Apaulgraham.com%20%22m...
If they're useful, you'd think one of the best hackers would have said something about it. He spent a good deal of his career talking about closures, types, objects, functions, computing theory, lambda calculus, etc. But no monads.
So, are they useful? Would anyone please give examples of their usefulness?
EDIT: You know what's worse than hero worship? Not being able to make a reasonable argument which uses someone as an example without being accused of hero worship.
edit: pg likes dynamic languages, monads don't pop up there as much. They're more useful when you (1) you have special syntax for them and (2) the type system helps you keep track of what code is not in a monad, generically using any monad, or using one particular monad. So, you'll typically only see monads in expressive/high-level static languages
Another cool one is the continuation monad. This lets you stop a pipeline and return a partial result, along with a function that lets you resume where you left off. This approach is commonly used in parsing, where you want to stop and restart depending on the availability of input. Another use for the continuation monad is to transform a chain of nested callbacks into linear code, eliminating callback hell.
Monads are important because of the abstraction, not specific use cases. If you're thinking in terms of specific use cases, you're already wrong. The monad abstraction lets you abstract out all kinds of control flow operations that apply to types of the right shape. It turns out to be a very general abstraction - it appears in a lot of places. And it turns out to be a sufficiently-powerful abstraction to lift any Turing-complete control flow from functions on simple types to functions on the monadic types.
So, the abstraction is general and powerful. Why isn't it used in all languages? Because most languages don't have anything like the bookkeeping mechanisms necessary to make it syntactically lightweight. Haskell, on the other hand, is sufficiently not-terrible to provide enough facility for abstraction to actually take advantage of monads. (All programming languages are terrible. Haskell is just less terrible than most.)
We want to write functions that don't involve IO (which we call "pure functions"), but we want to be able to use these functions on values that have been tainted by IO.
Ex.
The function abs takes an Int and returns its absolute value. No IO there; it takes an Int and returns an Int. However, in a pure language like Haskell, an integer that has been obtained through IO is of type "IO Int". Because Haskell is a pure language, we see that value as "tainted" by IO; it any anything derived from it are irreversibly tainted. A monad lets us pull the Int out of the IO context, get its absolute value with abs, and then stick that back into the IO context. That way, IO-tainted stuff keeps its label warning us that it's IO-tainted, but we can still use pure functions on it. This is necessary because if abs returned an IO Int, we wouldn't know whether or not abs does IO. That's to say, we couldn't get pure values back when we process pure values with abs.
This can be done for any "context", but IO was the most pressing one, and monads are what made IO in pure languages easy enough for them to be usable for everyday problems.
Also, the Paul Graham worship here is pretty bad, but being suspicious of something just because he hasn't mentioned it in an article is really bad.
I dunno, looks like pg's words here are perfectly applicable to monads. Good enough for me!
IMO, you have to understand the problems monads were meant to solve before you can understand why monads are a good solution.
Here was one key to my understanding monads. In a purely functional language you want everything to be referentially transparent. That is, an expression should be able to be substituted for its value at any given moment in time. At the very least, you'll want the ability to denote which parts of your program are referentially transparent and which aren't.
This can be expressed as utopian vision: in a purely functional language we'd like everything — everything — to be a value.
We now need some way of translating code with side effects into code that returns a value. One reasonable abstraction is to say that the "value" is not the work itself — once the work is done it's out of the bag, after all — but rather the "value" is the machine which can perform that work.
To a mathematician this screams "functor." If you want to move between the two spaces repeatedly this screams "adjoint functors." There's some work you want to do in Space A that's more easily expressed in Space B, so you take a thing in A, transform it to a thing in B, do the work in B, and then transform back to A.
Think in, e.g., Ruby: sentence.split(' ').map(&:reverse).join(' ')
split(' ') and join(' ') aren't quite inverses of each other, but they are adjoint. These pairs take us from the land of space-separated strings to the land of arrays back to the land of space separated strings.
You see that all the time in programming and mathematics. I sometimes call it the "Super Mario Brothers" maneuver when I'm trying to teach it to students. Mario wants to get to the end of the level, so he hits a block, goes up the vine to cloud land, runs through cloud land, and then comes back down.
This is a bit hand-wavey, but it's "morally correct" IMO. I mention functors because I'm hoping you've seen the maneuver I'm describing and monads are a special type of functor.
Not sure if it will actually help anyone, but it did help me in realising that I had not understood some code, but had instead just glossed over it. The State Monad, for example. [2]
--
[0] https://www.coursera.org/course/reactive
Don't read the monad tutorials.
No really, don't read the monad tutorials.
Learn about Haskell types.
Learn what a typeclass is.
Read the Typeclassopedia[1].
Read the monad definitions.
Use monads in real code.
Don't write monad-analogy tutorials.
[1]: http://www.haskell.org/haskellwiki/Typeclassopedia String postIdString = getHttpRequestParam("postId");
if ( postIdString == null ) {
return null;
}
Integer postId = Integer.parse(postIdString)
if ( postId == null ) {
return null;
}
return db.getPostById(postId);
It gets even uglier when you can't just short-circuit out of the whole function in the error case by returning null. This pattern of checking for null between statements is captured by the Maybe monad. In Haskell it would look like this: runMaybeT $ do
postIdBS <- MaybeT $ getParam "postId"
postId <- hoistMaybe $ fromBS postIdBS
MaybeT $ getPostById postId
The Maybe monad implements the core pattern, then that behavior is extended to other contexts by the MaybeT monad transformer. This example just lets you handle failure without giving you an indication of where the failure happened. If you need error reporting along with that, you can get it very easily with the Either monad and it's corresponding transformer EitherT like so: runEitherT $ do
postIdBS <- noteT "getting post ID" $ MaybeT $ getParam "postId"
postId <- hoistEither $ note "parsing post ID" $ fromBS postIdBS
noteT "retrieving post from database" $ MaybeT $ getPostById postId
2. Monads in Haskell provide a way to isolate and control side effects. I gave a presentation about this almost a year ago.The presentation is built around two real examples of software engineering problems that I encountered in my career. One encountered in a Java project, and one in a Haskell project. The thesis of the presentation is that monads are much more useful when combined with Haskell's strong static type system and purity. This also suggest a pretty good explanation of why pg doesn't talk about them--he's more of a dynamic language guy.
Here are a few of the concluding bullet points from the presentation.
Monads can:
3. Help you prevent data corruption in concurrent applications in pursuit of shorter critical sections and reduced lock contenion.
4. Prevent bugs that might arise from confusion between different phases of execution.
5. Guide exploration of complicated problems
6. Expand your dictionary of concepts and ways of thinking about a problem.
Numbers 2, 3, and 4 are the result of a powerful static type system, purity, and having typed IO. Nothing you listed has anything to do with being a monad. They're just examples of things you can do with types.
Your numbers 5 and 6 are sort of accurate. It's true that you can say "Hey, I think this type might form a monad," and explore from there. But if it does, it's nothing greatly interesting - you just get to take advantage of a bunch of existing library code. (And a little syntactic sugar, in Haskell.)
Really? So you know and use row types, linear logic, rank-N types, impredicative types, red-black trees, generalized algebraic data types, continuations, kind-level polymorphism, pattern matching, pi calculus, the Curry-Howard isomorphism, linear programming, dynamic programming, mBayesian inference, support vector machines, context-sensitive grammars, Feistel networks, one-time pads, vEB trees...
Programs should be these beautiful mathematical models that describe things - stick something in, get something out. Stick the same thing each time and get the same thing out each time.
Unfortunately to be useful programs have to be allowed to deal with messy stuff that can have side effects or some other ugly situation, or read input from a user or other programs.
You use monads to be explicit about where your program is messy and where it is isn't messy.
That can be stuff like IO, or stuff like Maybe (I dunno if what I'm getting here will even have a result - if it does, then it'll be Just (the result) otherwise it'll be Nothing) but the point is there is some messiness in there and you want to know there is messiness, otherwise you dont know what parts of your programs have messiness in them.
Perhaps you can think of them as atomic actions in concurrent programming? You declare something as atomic because you want to know the stuff inside the atom at least won't be affected by the rest of the concurrent messiness, so you can think about the pieces inside it as a non-concurrent piece.
Suppose I had a Javascript array of functions. And I pass that array into another function that performs that series of functions in order while maybe doing something in between each one. For example, maybe I can pass that list into a function that checks to make sure the result of each call isn't null or undefined. Or maybe I can pass the same list into another function that checks to make sure none of the results are NaN. Would this be an example of monadic behavior?
In fact if you used jQuery, you already know what a Monad is, probably without realizing it. Consider the following code:
$('.myelements').filter(filterFunction).css('color', '#ff0');
You can follow along what this code does by following the dots. First, all elements matching the ".myelements" class are returned, these are then filtered, and the result of that gets a css modification.Every time you wrote a method that returns an object, you already did monads in principle. They can be used to chain things together. While Haskell provides semantics that explicitly deal with the mechanics of monads, pretty much every dynamic language has them.
Here's what it looks like in PHP, for example:
$obj
->bla()
->beep()
->borp();
You can use monads to make your code more concise, to simply chain function calls, or to iterate over a list, but they're also often used when sifting through databases since filtering and applying attributes to items. For example, an SQL-like database query might be expressed like this: table1.select(all).where({ ref : 1 }).orderby('id');
Of course, that's an API which is a bit dishonest since it acts as if the code really goes through all those items (which would be a performance disaster with huge databases), so when you see something like this it's often just a shortcut to assemble a query. And this goes for monads in general, they're really more of an API style. A monad itself is an abstract concept, a paradigm.> Also, it's suspicious that pg has never once mentioned monads
For a theory why pg specifically hasn't written anything about monads yet, here's an article that I believe was on HN in the past about the difficulties of comfortably using monads in Lisp: http://marijnhaverbeke.nl/monad.html - but I guess the short explanation would probably be that Lispers tend to deal with monad-worthy problems in other ways.
You're using jquery as a functor not a monad.
> No, you have a completely incorrect understanding of Monads.
To re-use your slightly ad-hominem argument structure: "No, you have a completely incorrect understanding of Monads!" Of course the word "completely" here is just an unnecessarily flamebaity word choice in describing either of us. Clearly, we both have some understanding of monads. Let's look at the facts instead:
A functor applies an operation to a wrapped value. But monads apply functions that also return a wrapped value. It should be easy to see why people think the jQuery chain pattern is indeed monadic.
For what it's worth, I'm far from alone in asserting that jQuery is indeed a monad.
Since Haskell people claim to be the only enablers of "true monads", they would of course nitpick about patterns implemented in other languages. But religion aside, the OP asked for the possible uses of monads, and there they are. That the example also shows functors is just an added benefit, though not at all relevant to the discussion.
And a lot of people think that the reason astronauts float on the ISS is because there's no gravity there.
> Since Haskell people claim to be the only enablers of "true monads", they would of course nitpick about patterns implemented in other languages.
Yes, because monad is an actual term and calling something a monad just because it sort of looks like it if you squint doesn't make it one. You can't write return in the 'jQuery monad', because there's no way to wrap arbitrary data types, only things like DOM elements. Something like a promise is closer to a monad, because you can turn any value into a promise, but IIRC there are still issues around it that I don't remember offhand.
He describes monads as "a loophole" in the idea of function purity. This isn't true; monads aren't a way to 'cheat' any more than passing an accumulator through like
factorial acc 1 = acc
factorial acc n = factorial (acc * n) (n - 1)
is. He says something about state and functions and closures that I can't understand and how the state is 'always different', and it's just nonsense as far as I can tell.He also calls values 'monads', which isn't accurate terminology at all; they're usually called monadic actions.
Yes, but they also must follow the monad laws.
>It should be easy to see why people think the jQuery chain pattern is indeed monadic.
It is not easy for me to see. In any of your examples are you returning the same encapsulated structure in your lambda you pass in? It doesn't look like it.
>For what it's worth, I'm far from alone in asserting that jQuery is indeed a monad.
Yes, a lot of people have a misunderstanding of monads.
>Since Haskell people claim to be the only enablers of "true monads",
I'm not a Haskell person. I write monadic code in both Scala and C# to great effect.
Think hard about what you could do then.
That is why the Haskell community sometimes dubs them "programable semicolon"
forward(5)
raiseArm(5)
however, your program needs to be single threaded, and needs to quickly respond to the shutoff command. One solution would be to make every function exits quickly, and gets called in a loop until its done. This means that every function is essentially an object with the methods: init(), step(), where step returns true when the action is finished.Suppose we define forward(int) and raiseArm(int) as described. We would like to be able to easily define a function that moves forward then raises the arm. This new function would be combined(a,b)=forward(a) >> raiseArm(b).
Now, imagine we have a function that moves forward until it hits a wall, then returns the distance traveled. We want to move forward until we hit a wall, then return to our initial position. If we could block this would look like:
x=forwardUntilWall()
backward(x)
Instead of having forwardUntilWall() return x, it can store x in itself and let the bind operator pass it to the init function of backward(). This would let us write the above as: forwardUntilWall() `bind` backward.
Using do-notation (which is just syntactic sugar), we could also write that as:do x<-forwardUntilWall() backward(x)
Which looks a lot like our blocking version.
Imagine that you have a function setArm, and you want to set your arm sequentially at variuas posistions. Normally you would do:
for (each x in posistions):
setArm(x)
Using monads, you could do: forEach (posistions) (setArm)
or using do-notations: forEach (posistions) $ \x-> do
setArm(x)
In Haskell, forEach is called "forM_", and needs to be imported from Control.Monad.Loops. Of course you could avoid importing it and simply define it yourself.Also, we are not restricted to combining our specific monad with only the general monadic operators. For example, we can easily define a function that will take 2 monadic operations and return a monadic operation that runs both of them concurrently.
It should give you an idea of what monads are, but probably won't tell you why they're useful. They pop up in lots of places in programming, but you might not notice them at first because most languages have a way around them. They're more explicit in Haskell because the type system is powerful enough to make them both explicit and easy to use, via the combination of parametric polymorphism and type classes.
Here are some examples of monads:
* Option types (sometimes called nullable types).
* Many kinds of collections: lists, arrays, trees (but not sets, strictly speaking, because some invariants don't hold when treating sets as monads).
* Types with logging attached (Writer monads).
* Continuations.
Furthermore, Haskell's list comprehensions can be thought of as syntactic sugar for monadic operations, in addition to the usual do notation.Monads are useful because they let you add context to normal values -- any context you want, really. Often, the context just gets called "state", but lots of things are state, really. The monad operations are also useful because they allow you to force a sequence of execution, which is partly why they're also so useful for performing IO: Haskell is designed so that the compiler may re-order operations when it makes sense, and bind lets programmers place a sequential order on parts of the code. This is why the "pipeline" analogy is sometimes used.
An excellent article on monads is "You Could Have Invented Monads", which guides you through discovering several different monads using practical problems: http://blog.sigfpe.com/2006/08/you-could-have-invented-monad...
As for pg, monads don't pop up too often in untyped languages. While you can implement the monadic operations in an untyped language, it makes much less sense to do so. This is especially true for impure languages such as Lisp.
That is to say that "xs ==> (\x -> f x)" Would take every element in the set "xs", pass each of them into f, and union the results into a single set.
setMap :: (Ord a, Ord b) => (a -> b) -> Set a -> Set b
but that's still more restricted than fmap.In other programming languages, I'm sure you already use the very same functions that are fundamental to monads; you just don't have a syntactic construct that says "Hey, these things share a similar structure!"
Most of the other replies focus on IO (and on the do-notation side of things), and so I'm not sure that they'll help your confusion.
We'll need to start with functors. Without getting too formal, functors describe "containers" that can hold objects of any type. A list is a functor, a "Maybe" is a functor, the result of a computation (i.e., "IO") is a functor… lots of things are functors!
And since functors can contain anything, they can certainly recursively contain themselves - that is, they can be nested! We can have a list of lists, or a "Maybe (Maybe a)" (hey,… that seems like an awfully redundant thing to construct). When we talk about "IO", we can have an "IO (IO a)": that is, we can describe a computation that returns to us another computation!
And with these functors that I've mentioned, we realize something interesting: We would often like to flatten these nested structures. We use "concat" to reduce a list of lists to just a list; how to reduce a "Maybe (Maybe a)" to a "Maybe a" is obvious; for IO, we might want to take our description of a computation that returns a description of another computation ("IO (IO a)") and convert that into a description of a computation that runs the first computation, and upon receiving the result of that computation (which is a computation itself), run the resulting computation, and then return the result of THAT as our final result.
Also, for these things, we have an idea of how we would inject a pure value into the structure. We can create a list with a singleton element, or "Just" our element, or the computation that does nothing interesting (no missile launches) and just returns our element.
Haskell is just wacky enough to let us use the same syntax for all of these rather different ideas (which only share the similarity of having the monadic structure)!
NOW, I personally don't think that the trouble that people have with monads is related to the abstraction and the shared syntax - I think it has more to do with the use of the use of Haskell's "do" notation. Learn what "do" notation is sugar for! Learn what "bind" (>>=) is (it's basically a neat combination of the two operations ("bind" and "return", for what it's worth) that I described above).
I can't promise you that learning monads is something necessarily pragmatic (except for the fact that I think Haskell is the perhaps the MOST pragmatic language, and you'll need to learn monads to be comfortable with Haskell). But the world would be a pretty dull place if we only did what was pragmatic.
Concretely, imagine computations that might fail. In Haskell we represent this by the "possibly empty container with one slot" called Maybe. Maybe Int is either "just" an integer written (Just 3) or nothing at all, written Nothing. Frequently you'll use many failing processes all at once to build a result and want the failure of any one of them to propagate. You might do a bunch of map lookups to build a Person.
-- lookup :: Ord k => k -> Map k v -> Maybe v
type Name = String
type Address = String
type Age = Int
data Person Name Address Age
type AddressMap = Map Name Address
type AgeMap = Map Nage Age
mkPerson :: AddressMap -> AgeMap -> Name -> Maybe Person
mkPerson addys ages name =
case lookup addys name of
Nothing -> Nothing
Just addy -> case lookup ages name of
Nothing -> Nothing
Just age -> Person name addy age
That pattern of diving down deeper into a potentially failing result happens to follow the Monad interface. We think of each possibly failing value as being able to "bind" a next action which is only carried out if the computation doesn't fail. In other words, we package up the Just branches as continuations. mkPerson addys ages name =
bind (lookup addys name) (\addy ->
bind (lookup ages name) (\age ->
return (Person name addy age)
)
)
The `return` bit makes the non-failing computation (Person name addy age) into a non-failing Maybe computation---it injects a non-monadic computation into a monad in "as boring of a way as possible". (Also note that `bind` is usually called (>>=) so you can use it infix.)In general, this pattern is so nice that there's some special syntax sugar for it called `do notation`.
mkPerson addys ages name = do
addy <- lookup addys name
age <- lookup age name
return (Person name addy age)
which desugars to exactly the same thing as the previous example.The crazy bit is that this pattern, once you become familiar with the monadic interface, is extremely pervasive. Usually when you recognize it in some kind of computation it sheds new light on both how that computation can be manipulated and how it can be combined with other kinds. It's not infrequent to combine monads into a complex notion like---"a potentially infinite non-deterministic failing computation carried out in context of some configuration data where we can log and deterministically open and close resources"... which is written directly as the type
ReaderT ConfigData (WriterT LogMessage (LogicT (ErrorT ErrorType (SafeT m))))
Or, we can define a parser as a potentially-failing computation which modifies some parser state in the context of the current parse newtype Parser a = Parser (ReaderT Context (MaybeT (State Stream)) a)
parsePerson :: Parser Person
parsePerson = do
name <- parseName
_ <- parseSeparator
addy <- parseAddress
_ <- parseSeparator
age <- parseAge
return (Person name addy age)The term monad comes straight from Category Theory, a branch of mathematics. There are some rules, and anything that obeys those rules is a monad.
From the point of view of programming languages, any set of values/objects for which a particular set of functions exists is a monad. See wikipedia for what they are, exactly.
Monads exist on both dynamic and statically typed languages, but it is more difficult to explain them on dynamic languages, where you don't have types to explain the properties.
So many things can be monads. For example, collections can easily be monads, for it is easy to define "join" and "fmap" for them (one of the two possible ways of defining a monad):
* "join", aka "flatten", means that if we have a collection of a collection (a list of lists of x, a set of sets of y, etc), we can "flatten" it one level (that is, get a list of x or set of y);
* "fmap" means that, if we have a function that changes the objects inside that collection, we can create a function that changes the collection so that the objects inside it are modified. For example, if we have a function from strings to numbers, we can create a function that converts a list of strings into a list of numbers.
A list comprehension in Python is an example of these rules put in practice, though when using "if" you are going beyond these rules.
There are other monads besides collection and the usefulness of monads is that they work like an interface or API that is shared by all monads.
Going to Python again, consider the syntax:
listC = [f(x, y) for x in listA for y in listB]
The function f doesn't care that x and y came from listA and listB, and if Python supported any kind of monad, then you could write something like this: monadC = [f(x, y) for x in monadA for y in monadB]
And that would work for any monad you passed. You would have abstracted away that you had lists, making your code more generic and, therefore, more reusable and less repetitive.To go with an example you use elsewhere, you have a monad containing a file descriptor, a read line function and a convert string to number function, then you'd get back a monad of a number:
monadN = [int(readline(fd)) for fd in monadFD]
Note that monads don't define a way to extract that number from inside them, but that really isn't necessary, as you can continue to apply functions: monadVoid = [print(n) for n in monadN]
If your original monad was a list of file descriptors, you'd print a list of numbers, one for each file. A Maybe monad (aka Option monad) would either contain a file descriptor or not, and you'd either print a number or not accordingly.Another type of monad, the Future monad (also known as Promise), executes the function they are given asynchronously. So the monad containing the file descriptor was a future monad, then the code above would be executed and finish, but the number could be printed well afterward. I'll come back to this example at the end.
The usefulness or particular monads depends on the context, but the ones I'm familiar with mostly help avoiding side effects in functional programming. If you don't avoid side effects, you have no use for them. If you do, you'll inevitably come up with something equivalent to monads like the IO or State.
And the usefulness of the fact that they are monads comes from the fact that you can abstract away the specific type of monad they are, and work generically.
So, coming back to our number-printing program, let's say we are using a Future monad so that the number gets printed asynchronously, and we wish to test the program. If we use the Future monad, we'll probably have to sleep for a while and then see if the number was printed or not -- a test that may fail if the program takes too long to execute (say, the file is on a remote computer and has to be downloaded first).
One alternative is that we replace the Future monad with the Identity monad. The identity monad just holds a value, so any operation on it is executed synchronously. We can test our program using the Identity monad, so we do not have to way, and run it in production using the Future monad, so our program runs asynchronously.
We can do that because they are both just monads.
How do you pronounce '4 + 5'? It's easy, because you don't try to pronounce '+', you pronounce 'plus'. With '>>=', there are a few names that all mean the same thing.
There are two primary names, 'bind' and 'chain', and a description 'feed':
bind (the result of) m to (the input of) f
chain (the result of) m into f
feed m to f
There are probably a couple more things that can be said, in different contexts, but these three cover the majority use case.
That those terms are not mentioned in the article (titled "What I Wish I Knew When Learning Haskell", so apparently meant for people being new to the concepts) is my actual criticism. The ability to instinctively replace those symbols with the appropriate terms is something that has to be learned - and easy to forget to teach. He's talking about "don't learn monads by analogies" because he's maybe not fully aware that he is using "bind", "chain", "feed" when actually reading those expressions - all of which are analogies.
One Type to rule them all, One Type to find them, One Type to bring them all and in the Lambda >>= them
The pronunciation is a sort of bestial hissing that is difficult to describe; when you say it correctly, the terminal may be slightly moist.
> How is "m >>= f" pronounced?
If I'm not mistaken ">>=" is the bind function and could be written like:
bind m f
Meaning that "m >>= f" is pronounced as "bind m to f". What it's doing is: main = do
m <- SomeActionReturningMonad
f m
Are you saying that a language that uses symbols isn't a language? Is Japanese a language since it uses symbols (glyphs? correct me if I'm wrong).If you are talking about programming languages specifically, do you believe that "plus" should be used in place of "+"? Now I'm not going to get absurd and assume you mean using "parenthesis_start" in place of "(", but just in case... I will ask if you are.
Also, in Haskell specifically there are matching functions for those.
You can also search those symbols on Hoogle[1] or if it is a builtin keyword the Haskell keywords page[2] can be useful.
1. http://www.haskell.org/hoogle/ 2. http://www.haskell.org/haskellwiki/Keywords
We call it bind, but there's no pragmatic point in writing bind in it's infix form because of the prelevalence of the pattern.
Would you rather have that, or
(m `bind` f) `bind` g = m `bind` (\x -> (f x) `bind` g)P.S.: I guess what I'm trying to say is - every normal mathematical expression has a way to pronounce it. "plus", "for all x", "there exists an x", "sum of", "element of", ...
Also here is a great explanation with pictures that helped me understand it:
http://adit.io/posts/2013-04-17-functors,_applicatives,_and_...
The result of the computation m bound to the function f. The result of that bound to the function g.
...is equivalent to
lambda x where the result of f of x is bound to the function g.
mkdir project
cabal sandbox init
cabal install dependency
cabal repl # for a ghci instance inside the sandbox
Recommended is still to generate a .cabal file and specify your dependencies there.[1] http://stackoverflow.com/questions/7961604/fixing-issues-not...
SET : Group
MAN : Lie Group
TOP : Topological Group
GRP : Objects
The last line should really be "GRP : Abelian Group"A hundred folks who probably haven't investigated how to effectively teach adults, try to explain monads in the space of 3 tweets (probably not the best medium).
And then we wonder why folks steer towards less abstract tools. For every person who visits which gains monad satori, how many have we turned away, never to return?