My brain just refuses to fully "grok" them. I still don't see why they're so awesome. I know I use them day-to-day - LINQ, the "List" abstraction (supposedly also a monad??) but I just don't see why it's important to understand them on this whole new fundamentally different level.
It's like - loops. I use loops every day. But if someone were to say - "Hey, did you know that loops are just the Andifuncsplursx abstraction applied on Crazofors?? This is why Crazofors in Haskell are so awesome" I still wouldn't "get" Crazofors or why I should care enough to attempt to "get" them.
I've just given up at this point.
Also, i dont think you need to understand crazofors, ot even monads in full generality, unless youre a hardcore library implementor. What people blog about and whats req'd to write an app are very different things
The nice thing about monads with respect to the haskell programming thing is that you don't have to know why they work to get their benefit. However, if you wanted to build your own you probably do need to grok them.
However, I'm not convinced that monads are the best way to proceed on the most part in programming advancements. The immutable/mutable tracking, affine types, and borrow checker in Rust seems like it would help prevent a lot of the same types of bugs that pure FP does, but without the cost of needing to have a monadic effect system (and Rust is a more familiar style of programming than the State monad with a host of monad transformers). I'm hoping for optional affine types and more immutable data structures to become the norm more than I am monad usage.
Given up trying to understand Monads, or given up Haskell? I'm still confused by many monads, after years of Haskell programming; it hasn't stopped me getting stuff done.
You mention LINQ and the List abstraction. Yes IEnumerable<T> is a monad (LINQ isn't in itself - it [the grammar] is the equivalent of 'do' notation in Haskell). Monads are simply 'wrapper types' that follow a couple of rules:
1. You must be able to construct one from the un-wrapped value (return in Haskell, new List<T>(...) in C#)
2. It must implement the bind function.
If you understand 'map' (or Select in LINQ) then bind is very similar, except instead of returning a mapped version of the wrapped value, it returns a mapped version of the wrapped value, re-wrapped.
So in C# parlance (because I assume from your comment you use this daily):
IEnumerable<R> Map<T, R>(this IEnumerable<T> self, Func<T, R> mapper)
{
foreach(var x in self)
{
yield return mapper(x);
}
}
IEnumerable<R> Bind<T, R>(this IEnumerable<T> self, Func<T, IEnumerable<R>> binder)
{
foreach(var x in self)
{
foreach(var y in binder(x))
{
yield return y;
}
}
}
You may recognise that as SelectMany in LINQ. Select and SelectMany are special case function names in C# that allow the formation of LINQ expressions: var res = from x in list
select x + 1;
Equates to: var res = list.Select( x => x + 1);
And: var res = from x in list1
from y in list2
select x + y;
Equates to: var res = list1.SelectMany(x => list2.Select(y => x + y));
The result is a 'flattened' IEnumerable<T> - and that's why this is also known as flat-map.It's been a while since I've done any Haskell, so forgive me if I get some of the syntax wrong here. But the LINQ statement above translates very similarly:
do x <- list1
y <- list2
return x + y
(I know there's a list comprehension syntax in Haskell, but I assume this is still right, Haskellers?)The syntax is saying 'get the value out of its wrapper (the list) for me, so I can use it as-is (the add operation), then put the result back in a new wrapper'.
So why do we do all of this? It's so you can write the functions once that behave on the wrapped types, whether they're integers, strings or any type. The monad concept allows you to create a 'chain' of behaviour that from the outside is opaque, but internally it's operating on the raw wrapped values, as-is; and that makes all of the existing functions available. This is one of the key benefits.
A good analogy is to call them 'programmable semi-colons', because what makes each monad type what it is, is the behaviour of bind and return. So a list monad (as seen above) knows how to iterate the collection - so you don't have to write a for loop every time, an option monad knows when to not invoke the bind function if it's in a None state - so you don't have to test it yourself with an 'if' every time, the state monad knows how to propagate state changes - so you don't have to have extra context arguments on every function down the hierarchy, etc.
They remove boilerplate and capture common patterns. That is the other key benefit. At its core is an abstract idea, and I think that's why it's sometimes quite hard to grasp; but it's just a design pattern. Forget the category theory side of it, you don't need to know any of that to use use them or write new ones.
If you're more used to C# than Haskell, then you may want to check out my 'C# functional language extensions' library [2] that has C# implementations of the following monads:
Option
Either
State
Reader
Writer
And a few others, but that should be enough to get started. It may help you get your head around it in a language you're more familiar with?
[1] http://adit.io/posts/2013-04-17-functors,_applicatives,_and_...
I learned LINQ/Select(Many) and later all the map/filter/reduce functional goodness by playing with Clojure and never had a problem and never heard the word "monad" and was fine, totally fine.
Later watching a video on Rx (MS's reactive extensions) and hearing Erik Meijer talk about monads and Haskell and how Rx was inspired by that is what led me to try to learn Haskell. I just didn't see the "connection" between the functional and reactive patterns I was applying daily without any kind of mental problem and the Haskell stuff which was supposedly "the same", yet so....abstract?
I need to read your sample code and the links more carefully, but maybe this is the "key" I was lacking. Thanks !!!!
public class Option<T>
{
public readonly bool HasValue;
public readonly T Value;
internal Option(bool hasValue, T value)
{
HasValue = hasValue;
Value = value;
}
public Option<U> Select<U>(Func<T, U> map) =>
HasValue
? Option.Some<U>(map(Value))
: Option.None<U>();
public Option<V> SelectMany<U,V>(Func<T, Option<U>> bind, Func<T,U,V> project) =>
HasValue
? bind(Value).Select(u => project(Value,u))
: Option.None<V>();
}
public static class Option
{
public static Option<T> Some<T>(T value) =>
new Option<T>(true, value);
public static Option<T> None<T>() =>
new Option<T>(false, default(T));
}
The SelectMany implementation is slightly more complicated than I showed before. This is an optimisation that C# does to group the bind and map together. So it may look slightly scary as a function. But hopefully you can see that if the Option<T> has a value then it first invokes bind, then uses the result of the bind (an Option<U>) to project the final result. Here's a more imperative version of it: public Option<V> SelectMany<U, V>(Func<T, Option<U>> bind, Func<T, U, V> project)
{
if (HasValue)
{
var u = bind(Value);
if (u.HasValue)
{
return Option.Some(project(Value, u.Value));
}
else
{
return Option.None<V>();
}
}
else
{
return Option.None<V>();
}
}
You can see that with the IEnumerable<T> version of Select and SelectMany it encapsulates list iteration. With the Option monad it doesn't do that. It instead checks the HasValue field, and if it's false then it doesn't run the map or bind functions.The second static class: Option, contains the 'return' functions: Some or None. These wrap a value of type T in an Option<T>.
Now if we use Option<T> in a LINQ expression:
var option1 = Option.Some(10);
var option2 = Option.Some(10);
var none = Option.None<int>();
var res1 = from x in option1
from y in option2
select x + y;
// res1.HasValue == true res1.Value == 20
var res2 = from x in option1
from y in none
select x + y;
// res2.HasValue == false
var res3 = from x in none
from y in option2
select x + y;
// res3.HasValue == false
This is the same as using do notation in Haskell: do x <- option1
y <- option2
return (x + y)
If we were to do that imperatively it would look like this: var res = Option.None<int>();
if( option1.HasValue )
{
if( option2.HasValue )
{
res = Option.Some(option1.Value + option2.Value);
}
}
Clearly more cluttered and error prone and importantly, not composable. This is where the notion of 'programmable semi-colons' comes from. It appears that the monad is running behaviour 'between the lines', and it is.Hopefully that clears the fog. I'll keep an eye on this thread for a few days, so feel free to drop any questions in here or on my project page.
It's essentially a hidden argument that's passed around between the environment (the stuff in "do ... ") and the actions (the stuff called in "do ...", e.g, "x <- foo", foo would be the action). The argument is always the same type, and the each action can create a new one based off the old one. So you have IO, which is essentially "all interactions with the outside non-pure world", and the versions of it are "the world before I did this action" and "the world after I did this action".
Stuff that doesn't use the hidden arg doesn't have the action type (e.g., IO ()), so it has to be lifted, which essentially passes the old world state verbatim to the next step.
addToState :: Int -> State Int ()
addToState number = do
currentState <- get
put $ number + currentState
How is this working exactly? Well who cares. I know that once this function is called, my state will have been incremented by the given amount.Precisely. Then once you've used Haskell in many projects and gotten a handle on using it, you can figure out the relationships/laws and use it to even greater effect.
Why do you want to "grok" them? Just use them. In fact I'd say there isn't much more to grokking them than just using them.
You remind me of my first ground school instructor. I'd asked her why it was necessary to use rudder in a turn, and she waved her hand dismissively. "Just step on the ball" she recited, referring to the turn-and-bank indicator. No thank you, I'd rather know what's keeping my plane stable, so I found the answer elsewhere: Langewiesche's awesome Stick and Rudder, still relevant 70 years after publication.
Much lesser than not understanding ruddering into a turn I'd guess, though I don't know what it is. What do you think?
While it's tempting to characterize some programmers as just coders, that sounds pejorative and wouldn't be very charitable of me. So instead I'll distinguish programmers as tool makers and tool users. I know which one of those I'd put my faith in too, all things equal.
No offense intended... at all! I don't grok monads either, but in my world (mainframe stuff) Haskell doesn't register. If I used monads though I'd surely be driven to understand what's going on under the hood.
Forgive me another tangential OT story, an anecdote I read in a magazine many years ago. A man was spending a Saturday afternoon puttering around in his back yard while the family's hound slept on the back porch. The man called the dog, who then roused and put his nose to the ground, retracing all the steps his master had taken that afternoon until he finally reached the man. "The dog didn't give a damn about coming to me" the man growled, "He just wanted to know how I got here."
Hee! That's me.
Just forget the math, look at all the squares, triangles and circles. When you start to notice the sameness between ovals and circles and how they aren't squares you start to get the notion.
When it comes to monads, it's just the mechanics behind why some things, are intuitively composable. You don't need the mechanics if you just get a feeling for it.
In linq this is the feeling that you can stack a bunch of from x in xs from y in ys from ... together and get sensible results without too much thinking. As long as the xs and ys implement SelectMany as a monad it doesn't matter what it does, be it transforming collections, building sql queries or scheduling async tasks, the safe feeling will be there.
If you really want to do the math it might help to identify the level of abstraction we are talking about. For this I found it easier to reach for abstract algebra. Lookup the concept of a monoid and how it relates to you everyday arithmetic, it's roughly the same abstraction leap as the difference between your code and the concept of monads.
Now, monads really are just monoids for a particular kind of binary operation and values. The problem is that understanding monoids in the context of multiplication and addition is easy, you already have good grasp of both the arithmetic and the algebra there. But the compositions that monads describe (kleisly arrows) are probably not something you think about most of the time. Which kind of is like trying to understand abstract algebra without a good grasp on algebra.
This is what helped me most.
at that point, you might say "but this thing they're calling a monad in js is just an object.". well, these things we call monads in Haskell are just type classes.
now, a more fundamental question is "why do we care about types so much," and I don't have a pat answer for that one. suggestions to try out lisps for comparison makes sense, tho.
1. Why is it important that a List is a monad?
A. Its not particularly important. Its really just pointing out that monad is a very general abstraction - it wont tell you anything you don't already know about Lists.
On the other side, lists as kind of trivial examples of monads - they didnt really help me understand monads either.
Its like saying 1 is a real number - true, but it wont help you understand real numbers.
2. Why should i use monads?
A: I like to think of monads as things you can use in a for-comprehension (or the do notation in Haskell). If you can imagine writing something like
for {x <- thing; y<-thing} x + y
for "thing", then "thing" might be a monad (assuming all the math laws work out - sometimes they don't). In scala, for-comprehensions are literally de-sugared into maps/flatMaps/filters so for comprehension without filter <=> monad.3. But a monad is just a monoid in the category of endofunctors?
A: There is deep category theory and math behind this stuff. It can be useful to talk about it, but when you are starting out its overkill. Don't worry about "getting" the really abstract crap at first, just skim right over. Programming in monads is a lot easier than the theory, and the theory can be learned after getting your hands dirty. Using monads is mostly just for-comprehensions.
ps:
"monoid" = there is a zero, and an add operation. like integers with +, or integers with *).
"functor" = thing that has a map() operation.
"endo" = self
"category" = kind of like a set. its a container.
So a monad has a "zero" or default monad, a way to add monads, and a map operation that returns another monad.
e.g List -> zero = Nil, add = append, map = the "normal" map with f applied to each element
Future -> zero = empty Future, add = do future2 after future1, map = make a future with the f applied to value
let liftF = fmap
liftF negate (Just 1)
liftA negate (Just 1)
liftM negate (Just 1)
Realizing these all performed the same operation (if operating on a monadic value), and that `fmap` is just a `liftF` ("lift a regular function to work on a functor"), cleared some things up for me.This also helped: https://en.wikibooks.org/wiki/Haskell/Applicative_functors#A...
So all these abstractions really just let you do different things with functions that normally don't take functor arguments. They make the function basically allow functor arguments (kind of), to save writing a lot of boilerplate within your functions; typically boilerplate that would be put at the start of every function.
Once someone really gets the idea of what a functor is and why they can be useful, I think the rest is easy to understand once you read the definition of monads and applicatives.
That means really understanding the concept is much like understanding other algebraic abstractions.
The most basic concept in classic 19th century abstract algebra is "group." Just like the monad concept, this concept involves a set of values that can be combined with a few carefully chosen operations.
Just like with the monad concept, the group concept doesn't lend itself to immediate grokking. So a lot of people get frustrated by abstract algebra. They feel like someone just isn't telling them what a group "actually is."
But group is an abstraction over concrete "implementations". It is a common base for many algebraic topics, like integer arithmetic, modular arithmetic, matrix arithmetic, polynomial arithmetic, and even more complex structures. If you are familiar with the theory that applies to groups in general, you have access to proofs and formulations that can be applied to many different topics. Sometimes that generality is useless, sometimes it is very productive and succinct.
What the groups have in common are a binary operator that's associative (like plus or times) an identity element (like zero or one) and a way of taking inverses. This is all codified as group axioms. If you just look at those axioms you might say "so what?" but the concept is born from actual mathematical practice and is significantly useful and interesting.
Monad is an abstraction over different computational topics: I/O computations, randomized computations, failing computations, and so on. It captures in an elegant and abstract way the operations and elements required to express these topics. General functions can be written polymorphically over all monads, just like theorems and computations can be written to work for all groups.
So for a monad, you need return, which lifts a base element into the monadic class of values. (This notion of having a base element and a lifted set, for example Int and Maybe Int, is itself a basic abstraction that monad builds on, namely the functor abstraction, whose only operation is fmap, an abstraction of list mapping.) And you need bind, which is some way of combining one monadic value with a function producing another monadic value.
Those operations need to work together in reasonable ways specified formally by the monad axioms or monad laws.
Again, you can look at all that definition stuff and say "So what?" But again, the concept makes sense, it is useful, and it is born from abstracting over concrete topics. (Moggi wrote the first paper about the usefulness of the monad concept in computer science; the concept originally came from category theory, which is kind of like abstract algebra.)
The do notation is a good example of the usefulness of having an abstract type class for monads. It gives you syntactic sugar that works in a well defined way across many many topics.
Abstract algebra doesn't make any sense if you don't know how to work with plus and minus. Monads don't make sense if you're not comfortable with implementing pure combinators for simple computational structures.
So you should find some way to practice some of those basics, and then the abstraction "monad" will have meaning and not just look like a random assemblage of made-up rules.
Look at an example of using do notation with Maybe types to express failure. It's not that amazing, but it's useful enough, and makes sense. Now learn to implement the same thing from scratch without the syntactic sugar and without the monad functions. You will first write the whole thing with explicit pattern matching, tediously. Then you will implement the crucial combinators that lets you "bind" one Maybe value to another computation returning a new Maybe value. Then you can look at the source code for the Maybe instance of Monad and see that it is just the combinator you have written.
Then you can study the State monad in a similar way.
And then you can notice how the general monad functions are useful for both of those topics, failure and statefulness.
The concept monad in the context of category theory is even more abstract, but there's no need to worry about that level of abstraction merely to learn Haskell programming.
The reason monads are a big deal in Haskell is that the computational structures they conveniently express happen to be those which are otherwise described with "imperative" language features: mutation, jumps, and side effects.
So if you're interested in expressing those computations in a pure way, you should be a bit curious about the monad concept. If you're not interested, that's fine, but it's close to Haskell's reason for existing, so that's a more basic question: is it interesting to write programs in a pure way? If you say no, you're right to give up on Haskell.
A monad is just a type with 2 functions defined. Like an interface with two methods in OO langages. The 2 functions have to respect some laws but you can imagine whatever implementation for the 2 functions as long as the laws are observed (et type signature of course).
You could invent a total different implementation for the list monad, for the maybe monad, etc (if you respect the laws). There is no hidden ultra powerfull meaning which implies only one implementation.
The best paper on monads i ever read is an ascii art one, by Graham Hutton : http://www.cs.nott.ac.uk/~pszgmh/monads
Could you show such a different implementation for either list or maybe?
newtype HeadList a = HeadList { getHeadList :: [a] }
instance Monad HeadList where
return a = HeadList [a]
m >>= f = HeadList $ fmap (head . getHeadList . f) (getHeadList m)
This is a list instance that only keeps the head of function result, so it's basically just a map.But you could imagine putting any function that returns one result. min max avg normalize, etc
-- const [] for a HeadList
quux :: a -> HeadList a
quux = HeadList . (const [])
*Main> HeadList "hello... no wait" >>= quux
HeadList {getHeadList = "*** Exception: Prelude.head: empty list
Such breakage is verboten. This monad instance does not fly.(Yes, I know the history of the term.)
DrRacket is a great environment to learn programming and play with various concepts, both for beginners and advanced users alike.