A Burrito Is a Monad
williamcotton.com
williamcotton.com
class Monad (MonadTutorial badAnalogy) where
return badAnalogy = MonadTutorial badAnalogy
(MonadTutorial badAnalogy) >>= attemptToExplain = attemptToExplain badAnalogy
attemptToExplain :: Analogy -> (MonadTutorial worseAnalogy)
attemptToExplain analogy = MonadTutorial (makeItWorse analogy)
Been like 10 years since I've used Haskell so I'm not totally sure this works but it was fun.Some people call futures monads.
But the real takeaway is that people pretend they want you to understand all this type stuff, but they don't, they want to make up new complicated words for obvious ideas that you already understand, so they can feel smart. Once you understand it they aren't "smarter" anymore, so they make sure to explain every weird term they made up with 5 other weird terms they made up and insisting that all this abstract theory is relevant to everybody when it's not.
No it isn't.
A monad is a simple and easy concept that make it possible to write generic code that works across a number of very common things in software. The Monad interface is simple, and of immense practical utility.
There are only two things that are difficult about the concept:
1. The interface is so simple that it's hard to understand the purpose of it when you are looking at the interface disconnected from the concrete concepts that it abstracts over.
2. The interface was based on category theory concepts and created by people that have strong intuitions about category theory, so there is a lot of text floating around that presumes that background.
The essential thing here is that you don't need to know any of this in order to make use of the Monad interface or the generic code written against that interface.
> they want to make up new complicated words for obvious ideas that you already understand, so they can feel smart
> insisting that all this abstract theory is relevant to everybody when it's not.
Where is all of this bitterness coming from, my guy?
I think you are very confidently wrong here. Monads are not about wrapping types, as evidenced by the IO monad. You cannot say that a type is "wrapped" by the input-output system of a computer any more than you can say that a keyboard contains all possible character strings.
I am sorry to say but your comment is a cliche that's existed for at least the 15 years I've been using Haskell, where someone who doesn't understand a concept shows up, decries it as being a hoax and just a bogus complication on top of something simple, and while doing that displays not understanding it in the slightest.
But this existed before Haskell too, people have been doing what you're doing on the topic of structural programming, memory safe languages, regular expressions, dynamic html, ... html in the first place, microkernels, linux, unix, personal computers, minicomputers, computers, typewriters, looms, and electricity.
Understanding how state and IO can be represented as monads, and thus what useState() and useEffect() do in React, was also a nice consequence.
But certainly you are free to dismiss this piece of theory, and think about all these things as separate and peculiar. It usually very well suffices
A monad is a specific variant of a higher kinded type whose primary property lies in the fact that its value can be "evolved" via a specific function application (usually known as the "bind" operation). This sounds confusing but it's really a simple concept: if you take a higher kinded type, you can think about the values within it as the data and the type itself as metadata or a context of some sort. For example, an int is a piece of data, but a `Maybe int` is the same data with the added context of it being possibly absent.
A monad is not the only higher kinded type. Probably the most familiar HKT to working programmers (even if they don't know it) is the functor, which is a type that can be mapped over (in the sense of map-filter-reduce). If you have a `Foo<A>`, you can apply a function to turn it into a `Foo<B>`. However, a limitation of the functor is that you can only affect the data at the individual item level, not the context as a whole, so if you put a list of five values in, you'll get a list of five values back.
If we want to affect the context as well, we instead need a type like the monad, where we can "bind" a function that takes the inner value and returns a whole new wrapper based on it.
Why is this useful? Well, it can express a lot of different things very neatly - for example, fallible computation. Let's say you have two functions that both return a Maybe monad. In a language with nulls, you'd likely have to do something like `a = foo(); if (a is null) return; b = bar(); if (b is null) return`, which is tedious and error prone. Monads lend themselves to composing such chains extremely well, so you can simply do `foo().and_then(_ => bar())` and in the end have a value that combines the result (success or failure) of both of those functions.
Null values = chains of null checks = monads! Futures = chains of callbacks = monads! Mutable state = chains of writes = monads! Sequential execution = chains of statements = monads! And so on and so forth. You can get pretty crazy with it, not that I would recommend it.
Monads seem complicated and scary because most mainstream programming languages don't have the necessary abstractions to talk about higher kinded types as its own thing, but in reality most programmers are using monads daily without even realizing.
A polymorphic type - "a type wrapping another type" - isn't the same thing as a higher-kinded types.
Higher-kinded types are what let you express, in the language, the very notion of "a type wrapping another type". A "List Int" is a type wrapping another type; a "Maybe Int" is another type wrapping another type; but we can also say that "List Int" and "Maybe Int" (and etc.) can be abstracted over as "Monad Int"s. Monad is a higher-kinded type because it's a type wrapping types wrapping another type.
If however I have a List<byte> and would like it to become Maybe<UInt128>. A loop is always a jmp of some sort, a bind though could become a SIMD operation or be passed off to a coprocessor and I as the programmer would be none the wiser since all I cared about was the end result.
A monad is a type which support a particular pattern of function chaining. But the caining will have semantics which depend on the particular monad type, so it is difficult coming up with a metaphor which apply to all monads.
But I don't agree that a burrito effectively represents a monad purely because as you tried to allude to, a monad is not just about the fact that it is a wrapped time but about what functions are available to it.
Imagine a list with no map, is it really a monad?
https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/...
That whole series is a great breakdown of functors and monads that really opened my eyes. This is near the end but that also means all the links are available.
Side effects are not a type though. The “type wrapping another type” in the parent refers to List T and Maybe T where T is the wrapped type. But with IO you can have IO String or IO () representing operations with side-effects, so the analogy breaks down.
A common problem with half-baked monad explanations is they generalize from one or two monads and then create an analogy which breaks down for other common monads.
Well that's because they are. And it is one of my favorite examples to explain the power of monads actually.
Most humans prefer programming in the async/await style compared to using promises/futures with callbacks (commonly called "callback hell"). In most languages, you need explicit language support to enable async/await by adding new keywords into the language. (And you are completely at the mercy of your language designers. Looking at you Java.)
Haskellers, because they grok monads and futures are just monads, are able to code in an async/await style without any special syntax specifically for async/await. They just use the same notation they use for other monads.
They become like beautiful fractal trees with branches upon branches of people giving alternate examples and then comments refuting those.
I'm pretty sure at this point this sort of meta-in-joke art form is what a monad is.
It's almost as good as the old "time to crate" way of measuring first person shooters.
The issue is, monads is a pattern for function composition which can be used for various seemingly unrelated things. But people see one instance of it and then generalize wrongly. E.g. lists and option-types are monads so some generalize them as “wrappers around types” - except this doesnt describe the IO monad at all. Or some see the IO and State monad and generalize monads as about managing side-effects - except this does not describe list or option types.
In Haskell, you have types. Concrete types have names that start with an uppercase letter, and generic types have lowercase letters.
inc :: Int -> Int
inc x = x + 1
not :: Bool -> Bool
not True = False
not False = True
-- id returns whatever it's given unchanged,
-- no matter the type.
id :: a -> a
id x = x
You also have more complex types: data IntOrChar = AnInt Int | AChar Char
You can match on the "AnInt" part or the "AChar" part. They're like tags. -- Increment it if it's an Int, otherwise leave it
f :: IntOrChar -> IntOrChar
f (AnInt n) = AnInt (n + 1)
f (AChar c) = AChar c
The letter "n" is the name of a variable of type Int, and "c" is likewise the name of a variable of type Char.You can have generic data types in the definition of a data tyñe and still add those tags. Generic type parameters are lowercase, just like in functions.
data Maybe a = Nothing | Just a
You could have functions that define the a type explicitly: someFn :: Maybe Int -> Maybe Int
Or it could be generic over that type parameter: someFn :: Maybe a -> Maybe a
"Maybe" is a type constructor rather than a type itself. You can also have other type constructors, e.g. this binary tree definition (which is recursive in this case): data Tree a = Leaf a | Branch a (Tree a) (Tree a)
In function definitions, you can be generic over type constructors, too. You would write it like this: someFn :: t a -> t a
That "t" could be a Maybe, or a Tree, or a list, or any type constructor.In Haskell, Monad is basically an interface where it implements two functions: the infix function ">>=" and the function "return". The interface looks like this:
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
">>=" takes two parameters: some "m a" value, and a function that takes an "a" and returns an "m b", then returns an "m b", where "m" is a type constructor like "Maybe", and "a" and "b" are simple types like Int.Here's how you could write the Monad instance for Maybe:
instance Monad Maybe where
return :: a -> Maybe a
return x = Just x
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
maybeX >>= f = case maybeX of
Nothing -> Nothing
Just x -> f x
Here's how you could write it for lists: instance Monad [] where
return :: a -> [a]
return x = [x]
(>>=) :: [a] -> (a -> [b]) -> [b]
xs >>= f = concatMap f xs type Burrito = Meat option * Ingredient list
Does `*` create a tuple type?Do lowercase `option` and `list` somehow specify `Option` and `List` types?
Yep, tuple or "product type."
Yep, in F# type annotations can be either in prefix style `List<T>` or postfix style `T list`.
(Same concept, from 2009)
Would have gone a long way to me understanding Monads, rather than some esoteric mathematical symbols.
If you type-check your Python/JS/Ruby code, monads are a functional-style solution to get rid of that code (functional programmers don't like type-checking).
That's my simplest explanation at least!
type Burrito = Meat option \* Ingredient list
let (>>=) burrito f =
match burrito with
| Some meat, ingredients -> f (Some meat, ingredients)
| None, _ -> None, []
let returnBurrito (meat, ingredients) = meat, ingredientsYou have an array of state, and you can call a method on it that returns a new array of state, that you can call a method on, that returns a new array of state, etc. And that concept of doing state -> function call -> state is the holy Monad.
A JS pleb would write instead:
burrito = (new Tortilla()).addMeat(Chicken).addMissionBurritoIngredients().holdThe(Cheese)
No one would be confused about what was going on, and it would be basically the same thing.
While it is just functions. To say, it's just functions all the way down, doesn't help you talk about them.
Could say that math is all functions, and having some language to discuss that subject is maybe more the purpose of the monad.
Kind of like if you were to say to a math student, just go study functions, no need for any school or language to describe what is happening.
But yes, category theory is a bit heavy handed to a programmer just needing to chain some functions.
Maybe the problem is such a vast gulf between the junior dev just needing to know how to chain some things, and the category mathematician that has never coded. Yet they are circling around the same subject.
There's a lot of monads in our lives: puzzels, baking, stories, symphonies, paintings, and projects.
I get that cooking is necessary to prepare the ingredients, but once the ingredients are all individually prepared, the cooking is irrelevant to making them into burritos.
Another mention of monads and burritos.
[0]: https://mostly-adequate.gitbook.io/mostly-adequate-guide/ch0...
// Python
new_foo = foo.bar().baz()
Work as / can be defined as monads, if you get a Foo out of each of them. This at least gives me something practical to hang the concept on.
let tortilla = returnBurrito (Some Veggie, [])
is a typo and it should be return Burrito
?Also somewhat familiar with FP but not this specific language (guessing Haskell?), is the `>==` operator essentially a "pipe" operator?
`return` is a specific function of a Monad, and it is not used like the keyword "return" in other imperative languages.
Monads are a design pattern, albeit not one in the Gang of Four tradition, and it's concrete enough to be expressible in Haskell's type system. Learn the operations and laws, study some code involving List or IO to see how it's used, and get to work. No big deal.
"Nah there's nothing to learning, you don't know something, badabing, you know something, big deal."