Maybe a better example than the common 'or nothing' would help in this case.
Maybe a better example than the common 'or nothing' would help in this case.
I had a great success understanding Monad as a sequential program. The simplest way to write such a program would be just to chain elementary statements with the >> operator, however, to do any kind of control this way you'd need to rely on implicit state (think of programming in Assembler where you do branches based on implicit flag registers set by the previous instructions and the branch target itself is implicitly a new program).
So Monad is just the minimal improvement over this that allows control structures without need of implicit conventions. It achieves this with three simple additions to the basic sequential model: 1. add explicit results to the statements 2. allow a switch after each statement based on its results 3. add pure elementary statements (that don't do anything other than returning the value they have been created with) so we could transform results without modifying the context.
You can still do the simple chaining of statements in the Monad but whenever you need conditional/switch - you can do it explicitly with the >>= operator, whose argument is essentially a switch table, that assigns a new sequence of statements for each value.
You still need to figure out how do the List monad takes multiple branches in its switches simultaneously but it might be easier for a programmer to understand than container-value analogy.
The payoff of the article is, I think, supposed to be:
getLine >>= readFile >>= putStrLn
Which looks great. But what I'd like to see is a longer explanation of why this is better than the alternatives (like simple function composition).
My goal was just to provide a gentle introduction.
There is a big problem that "Monads" can refer to the generalization and combinators that work with any Monad, or to specific Monad types and the style of composition that they choose to compose values together.
Monadic IO is a solution to combining referential transparency and effectful programs.
Moands in general is a solution to defining the same combinators over and over for each Monad.
That said, one thing I noticed is that there are two main "types" of monad tutorials. The first one is the the one taken by the OP and I call it the "category theory" approach. It tends to show what sorts of things you can do with monads and how they work mathematically but they kind of lack an explanation for why you should bother with monads in the first place.
The other approach for monads is the "monads as an useful interface" approach. One paper I would highly recommend in this category is the "awkward squad" paper by Simon Peyton Jones:
https://research.microsoft.com/en-us/um/people/simonpj/Paper...
Its an introduction to monads that helped me a lot and it also explains quite a bit about the history of monads.
Which lets me come back to your main point:
> why this is better than the alternatives (like simple function composition)
Because simple function composition doesn't work! While nowadays people like to point out that Haskell is a pure functional language, noone would have bothered with making a pure language (back then) if all you gained was all this headache with monads. The reason Haskell was pure in the first place was because its original goal was to be a lazily-evaluated language and that lazyness kind of forces you into pureness. You might think the order of evaluation of function arguments in C being unspecified is bad, but Haskell would be even worse since the order of evaluation for everything is unspecified and some things might not even end up being evaluated at all!
They ended up trying all sorts of approaches for adding effectful computation to Haskell and the most successful one was the monad one (the SPJ paper goes over this a bit). If you pay attention, the monad interface forces computations to be single threaded (you can't "branch out" monadic effects) and doesn't let you generally start or end an effectful computation wherever you want, meaning that whoever defined the monad you are using can control how effects are started or terminated via what functions they expose in the public interface (Maybe exposes the constructors directly so you can pattern match on them but IO makes it so `main` the only place you can start an IO computation)
Finally, this gets us back to notational problems. While monads make things really neat in the type level and as a platform for effects, it can end up really noisy in practice:
getNumber >>= (\a -> getNumber >>= (\b -> f a b)))
So for monads we have "do" notation that lets you write things in a neater way: a <- getNumber
b <- getNumber
f a b
This is really helpful and 99% of the time, do notation is precisely why people bother making things into monads instead of defining their own versions of (>>=), like they do in Javascript with promises. However, writing all those intermediate variables can be annoying and doesn't feel "composeable". The Functor and Applicative type classes provide methods to do that: f <$> getNumber <*> getNumber
Monads are always particular cases of these type classes and you can show this mathematically but its a bit complicated in practice because people added monads to Haskell before they discovered these otehr type classes were useful...To wrap things up I would like to point out that most of the time monads and functors are an "advanced" technique that lets you take programs that you could already write before but write them in a neater way, with do notation, fmap, <$>, etc. (While its true that you can write generic programs that abstract over the monad interface, you only end up doing that if you are writing a control flow library or something even more advanced like a parser combinator). To give examples of what I am talking about, if you have functions that return null on error you can already get by with adding an if statements after every call (like you have to do in C) but monads let you omit the boilerplate if statements. A more advanced example I like is coroutines/generators (things that "yield"). You can write this sort of program in a normal language by manually inverting your control flow, storing state explicitly in stacks or other data structures. A continuation monad lets you write the code in a more natural way (and as a library, without needing to extend the base language with suport for a yield statement!)I think the biggest problem with monad tutorials is that it seems that everyone finally "gets" monads in a different way so the monad tutorials that get written lack motivation. For example, I finally "got" monads when I was working with a promise library in Javascript...
That said, one thing I noticed is that there are two main "types" of monad tutorials. The first one is the the one taken by the OP and I call it the "category theory" approach. It tends to show what sorts of things you can do with monads and how they work mathematically but they kind of lack an explanation for why you should bother with monads in the first place.
The other approach for monads is the "monads as an useful interface" approach. One paper I would highly recommend in this category is the "awkward squad" paper by Simon Peyton Jones:
https://research.microsoft.com/en-us/um/people/simonpj/Paper...
Its an introduction to monads that helped me a lot and it also explains quite a bit about the history of monads.
Which lets me come back to your main point:
> why this is better than the alternatives (like simple function composition)
Because simple function composition doesn't work! While nowadays people like to point out that Haskell is a pure functional language, noone would have bothered with making a pure language (back then) if all you gained was all this headache with monads. The reason Haskell was pure in the first place was because its original goal was to be a lazily-evaluated language and that lazyness kind of forces you into pureness. You might think the order of evaluation of function arguments in C being unspecified is bad, but Haskell would be even worse since the order of evaluation for everything is unspecified and some things might not even end up being evaluated at all!
They ended up trying all sorts of approaches for adding effectful computation to Haskell and the most successful one was the monad one (the SPJ paper goes over this a bit). If you pay attention, the monad interface forces computations to be single threaded (you can't "branch out" monadic effects) and doesn't let you generally start or end an effectful computation wherever you want, meaning that whoever defined the monad you are using can control how effects are started or terminated via what functions they expose in the public interface (Maybe exposes the constructors directly so you can pattern match on them but IO makes it so `main` the only place you can start an IO computation)
Finally, this gets us back to notational problems. While monads make things really neat in the type level and as a platform for effects, it can end up really noisy in practice:
getNumber >>= (\a -> getNumber >>= (\b -> f a b)))
So for monads we have "do" notation that lets you write things in a neater way: a <- getNumber
b <- getNumber
f a b
This is really helpful and 99% of the time, do notation is precisely why people bother making things into monads instead of defining their own versions of (>>=), like they do in Javascript with promises. However, writing all those intermediate variables can be annoying and doesn't feel "composeable". The Functor and Applicative type classes provide methods to do that: f <$> getNumber <*> getNumber
Monads are always particular cases of these type classes and you can show this mathematically but its a bit complicated in practice because people added monads to Haskell before they discovered these otehr type classes were useful...To wrap things up I would like to point out that most of the time monads and functors are an "advanced" technique that lets you take programs that you could already write before but write them in a neater way, with do notation, fmap, <$>, etc. (While its true that you can write generic programs that abstract over the monad interface, you only end up doing that if you are writing a control flow library or something even more advanced like a parser combinator). To give examples of what I am talking about, if you have functions that return null on error you can already get by with adding an if statements after every call (like you have to do in C) but monads let you omit the boilerplate if statements. A more advanced example I like is coroutines/generators (things that "yield"). You can write this sort of program in a normal language by manually inverting your control flow, storing state explicitly in stacks or other data structures. A continuation monad lets you write the code in a more natural way (and as a library, without needing to extend the base language with suport for a yield statement!)
http://web.cecs.pdx.edu/~antoy/Courses/TPFLP/lectures/MONADS...
A simple example in the blog, it talks about Just 2 or Just a but don't explain what Just is. Also 'a' as a parameter for type is not explained. Leaving the readers to wonder whether a is argument to a function or it is a value since Just 2 and Just a are mentioned in the same section.
Also a lot of tutorials are afraid to draw parallel concepts from other languages that would be familiar with the readers, instead preferring to invent abstract definition to build things up. Most people learn by relating and inferring from familiar concepts.
E.g. Functor is just like template in C++ for constructing type. As a template it has placeholders to let the programmer to fill in to build a concrete type. These placeholders are the parameters to the functor. The programmer supplies a type as the parameter to the functor to construct a concrete type. E.g. "List a" is a functor which is called List and its parameter is a. "List int" would build a concrete type for values of list of int.
Maybe and Just are not a good introduction functor for beginners. Few other languages have a similar concept and people just roll their eyes when they learn what it is since it's not as useful in general.
List is a better introduction to functor. Most programmers have work with List and know what it is and its problems relating to its element type. Seeing "List int" people immediately know it's defining a list of int, and they know right the way what functor is and what it is for. Doing a couple more examples of List float, List string, and people will see why functor is very useful. May be adding Tree or some other functor operator like Eq or Show to how to have generic behaviors applying to any types.
No, a type variable is! For instance, if we have a data type:
data MyBox a = MyBox a
a is the type variable, and is pretty much the same thing as a C++ class or struct template that stores a (const) value: template <class T>
struct MyBox {
MyBox(T const &nv) : v(nv) {}
T const v;
};
This has nothing to do with functors (yet). MyBox has a so-called kind of MyBox :: * -> *
meaning that MyBox takes a type (namely a) and returns a type. For instance, MyBox Int has the kind: MyBox Int :: *
Now, a Functor is any data type of kind * -> *
for which we can implement the function fmap fmap :: (a -> b) -> f a -> f b
Such that: fmap id = id
fmap (p . q) = (fmap p) . (fmap q)
hold. We can implement this function for MyBox and show that the laws hold (left as an exercise to the reader): instance Functor MyBox where
fmap f (MyBox v) = MyBox (f v)
In other words, fmap applies a function within a context. And the fact that a particular context (such as MyBox) is a Functor states that the functor laws hold.