Skip to content

Instantly share code, notes, and snippets.

@t184256
Created September 23, 2018 20:02
Show Gist options
  • Save t184256/f23b2825e6eb58f88f149b117e5044d7 to your computer and use it in GitHub Desktop.
Save t184256/f23b2825e6eb58f88f149b117e5044d7 to your computer and use it in GitHub Desktop.
What is a monad, attempt 1 of several

Skippable intro

tazjin once told me something koanish in the lines of:

When a man 'understands' monads, he writes a blog post about it. And it takes three such posts for him to really understand monads.

This text is my first 'post' in this hypothetised progression. I anticipate missing the point, so the intended audience is not other neophytes, but seasoned FP folk looking at me condenscendingly before replying "you're not there yet".

It's written amidst the chapter 13 of 'Learn You a Haskell for Great Good', 'For a Few Monads More'. This reading is the entirety of my relevant experience at the moment, and a bit after the dissection of the State monad example and the introduction of the Either one, I nearly heard the first click inside my head, as I finally could generalize an abstract concept of a monad from examples. So I thought: Hey! Click one. How many to follow? Let's check tazjin's hypothesis!

So, what is a monad? (understanding level 1)

A monad is a transformation enhancer.

Before you blast off with "that's function composition, or basically anything in FP, you fool", allow me to reiterate that a bit differently. Four times, actually:

  1. At a birdseye, any specific monad is a transformation enhancer.
  2. Semantically, that monad is a semantic ruleset for this enhancement.
  3. Technically, it's also a piece of code that helps carrying out this ruleset.
  4. And finally, it is the semantic meaning attached to the underlying datatype.

One could say that a monad [in general] is the transformation enhancer, but tackling such a thing would be way too many axis of 'genericity': Any [axis A] monad is some [axis B] limitedly arbitrary enhancer of any [axis C] transformation... of somethings. Two axes, B and C are more than enough for now, so let's scan a bit over A.

Examples I generalized from and how I did it (also skippable)

A = Maybe

The probably-beaten-to-death Maybe monad enhances arbitrary transformation f with a rule of how to apply it to Maybe. Nothing becomes nothing, f (Just a) becomes Just (f a).

I have stated nothing about the fact that it's the only way of enhancing transformations on Maybe-like data structures, merely that it's a possible one.

What I do state, is the fact that it has semantic connotations that do comprise the mental image of Maybe in the head of a programmer. It's these very connotations that make Maybe a Maybe, and not just a fancy enum.

A = Either

Now the rule is "f (Left a) becomes Left a, f (Right a) becomes Right f(a)".

The fact that it's the very rule that makes Either an Either becomes more obvious here. Without it, it's just a datatype, symmetric with regard to Left/Right. Once this semantic rule is in, coupled with attaching an 'error details' meaning to the Left, one can start writing transformations that follow that convention.

In Haskell, it just happened to be codifiable in a way, that makes transformations conducted with >>= or do-notation auto-enforce these semantics. Maybe these could translate into error propagation operator, HOF, aspects or something entirely else in other languages and paradigms. These are technicalities, the underlying concept (senses 2, 4) is free of them.

An Either monad tackles this the Haskell way. Wrapping regular, convention-unaware transformations, Haskell monads carry out that convention. The monad is that rule, it is that semantic convention, but now it also gains language support.

Also one should note that monad Either has very little to do with the datatype Either. In-memory representation is not of interest here, the semantic convention is.

A = List

List monad enhances arbitrary transformation of values inside them by allowing to apply it over lists. The rule is operating on cartesian-products of arguments now. Infusing each and every transformation with this rule may be tedious, but monads (in the sense 3: enforcing aid),

= and do-notation 'application convention' conveniently auto-enhance all the transformations according to this semantics.

Sidenote: I will actually be surprised if that's the only 'application convention' for that, and there are no other ways to do that, e.g. by somehow 'lifting' singular operations to accept monads. Probably I just didn't read far enough yet and they are waiting for me on the next page.

A = Writer

Writer allows for attaching information to values, allowing them to be handled transparently... OK, semi-transparently from the viewpoint of the original transformations (senses 1 and 3). That extra information gets mappended together, so... an extremely generalized logfile (for senses 2 and 4).

A = Reader

Reader makes functions accept values that are just one step behind in the partial application chain and defers their actual application until that last value is supplied.

I see Reader as something extremely niche as of right now. Specific enough to retell, but too abstract to actually find it useful.

A = State

That seems like the least immediately useful one. Also, the naming is horrible; even Stateful seems better (but still wrong). This 'transformation enhancer' allows to use >>=/do chaining for chaining together state transformations that also emit an extra result (and binding it). At this moment I see it just as a syntactic sugar to reuse do notation for the purpose of composing them (with nicer-looking binding).

A = IO

IO, I suppose, is the most muddled and throws off the most. IO seems as overloaded as the abstract 'socket' word, that seemingly aims to be everything network-related at once.

My (limited) understanding is that the IO-the-monad allows chaining IO-the-actions into a big executable chain of side-effects. Executable is the key word, and while IO-the-actions do not seem too magical, I'd speculate that IO-the-monad is some hack that encapsulates the way of actually executing the side-effects. So, my guess is that IO-the-actions are the transformations, while IO-the-monad enhances these transformations with executability. But that's just a guess, I know too little about what they actually are.

How it ties with monad laws

Enhancing our transformation works necessitates 'enriching' the data being transformed in our 'enhanced' chain. return is the 'minimum enriching' of a value.

Left identity is "the transformation processes the minimally enriched value as if it was a regular one".

Right identity is "'minimum re-enrichment' is a no-op".

Associativity in my terms is "the resulting enriched transformation chain is associative", as it is a function composition chain, except our functions are transformations on steroids now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment