Haskell Is Not Programming

Introduction to the “Magical Haskell”

J Ho
Superstring Theory
7 min readOct 31, 2017

--

In this chapter: teaching haskell to kids * treacherous elephants * where is my for loop? * is there a better way to learn? * haskell in pictures

update: Chapters 1 through 5 of the book are ready, please read & give feedback!

Lately, I’ve been spending time with Type Theory and lambda cube. E.g., I’ve implemented SystemF-omega-ish type library in coffeescript “for the fun of finding out how things work” as Dr. Feynman used to say. Then I moved to designing toy functional languages. Haskell is great, but what would a great functional language look like if it were designed today? With all that we know from state-of-the-art Haskell development and research now?

the meaning of this will become apparent down the line

Two thoughts persisted:

  • “I̶ ̶n̶e̶e̶d̶ ̶t̶o̶ ̶w̶r̶i̶t̶e̶ ̶a̶n̶o̶t̶h̶e̶r̶ ̶m̶o̶n̶a̶d̶ ̶t̶u̶t̶o̶r̶i̶a̶l̶” — haha, got ya— “how do I teach functional programming to my kids?” — so that it’s light, comprehensible, fun and conveys the beauty of the concepts involved, — and
  • we’ve been doing it all wrong

Ok, the latter may be a gross exaggeration, but I need to drive the point across. As many unfortunate souls before me, I have gone down the painful but oh-so-typical road of coming from imperative OO background and trying to build bridges from the patterns learned there to a completely different functional world.

The problem with this is, your brain works against you

(do read the classic “Thinking, Fast and Slow” if you haven’t yet — at the very least, it will help you detect and fight your own biases)

Our brain’s “System 1” works in patterns. It is efficient and fast — much faster than the conscious “System 2”, which we are using when studying Category Theory. It is in contrast subconscious. It deconstructs and shreds all the elephants you see in your life into basic shapes, curves and colors, which makes recognizing elephants an easy task, until one day you glimpse upon a cloud like this —

— and your brain goes: “oh, look, it’s an elephant!”

Well, no, it’s a cloud.

And this is exactly the trap laid out for programmers trying to escape to the wonderful world of Haskell from the less elegant Java, C++, or that terrible-which-shall-not-be-named-but-ends-in-…avaScript backgrounds. The thought goes: C++ is a programming language, as is Haskell, so the concepts should be pretty similar, right? Isn’t it like Indian and African elephants: sure, one has bigger ears but aren’t they essentially the same?

No, they are not: one is an elephant and the other one is a cloud. You cannot reason about clouds using your elephant knowledge. You can ride an elephant, but attempting to ride a cloud would end really badly. Your brain constantly tries to find analogies between your previous experience and new knowledge — that’s how we learn, it is a good thing — but in this case the unfortunate fact that both Haskell and JavaScript are called “programming languages” is a disservice. Trying to find similar patterns in both leads to consequences only slightly less disastrous than trying to ride a cloud thinking it’s an elephant.

To really learn and master the power of Haskell, you need to forget everything you know about imperative programming (yes, really, everything). Build upon a different foundation. One of pure abstract math, starting from category theory, from set theory, from type theory, from Henri Poincare. You have to build Natural numbers with pen and paper starting with an empty set. Construct a Ring on them. Abstract away and discover Monoids. Start appreciating the beauty of internal structure of types and categories manifested in polymorphic functions and laws on them. Be amazed at how one abstraction can elegantly describe seemingly unrelated entities…

But who in the world has time for this?!

Well, exactly. This is one of the primary reasons there’s still a certain bias against Haskell among practitioners. A big part of Haskell community give impression of very smart monad-stacking-arrow-composing-scientist-magicians sitting in their ivory towers with too much time on their hands working out some abstract problems, while we need to code stuff that works. Even if it’s ugly.

Take heart, there’s hope! But before I outline a proposed (yet another) way out, let me illustrate the elephant vs cloud conundrum with a pretty typical “where is my for loop?!” situation.

Let’s say you are building a GUI library based on some low-level API and need to render lines of text on screen. You construct an array of textures where each corresponds to a new line of text. You need to render each one under the other, incrementing an y coordinate, like so:

int y = 100;
int step = 40;
for (int i = 0; i < numOfTextures; i++) {
renderTexture (texture[i], y);
y += step;
}

Something you’ve done a thousand times in different situations. You switch to Haskell where you have a list of textures textures :: [Texture] and your System 1 starts screaming “where is my for loop?!” and then “how do I pass the state when iterating?” Should I use mutable variables, IORef, MVar, TVar ? (no, you definitely shouldn’t, even though you can). I know I am supposed to map over lists, so should I use a State monad to increment y while mapping? (hold your horses, it’s definitely an overkill for a simple loop variable).

Only when you tell your System 1 to stop getting ideas from your imperative experience, consider abstractions, realize it is best to fold and traverse structures in the functional world, you come to something like:

foldM (iterate step) startingY textures
where iterate s y tex = renderTexture tex y >> (y + s)

… which gives you goosebumps after all the braces and semicolons in the c-land, but patterns have nothing in common: you iterate over arrays with mutable variables in one and you fold structures with computations in the other. Elephants and clouds.

So, how do we teach Haskell to kids or help adults master its’ power faster and more efficiently?

First, stop trying to build bridges from imperative world — it messes with people’s brains. Moreover, we need to explain real world Haskell programs design patterns by contrasting them with imperative world, which is largely missing now. But this needs to be done only after a student has learned functional approach and small patterns from scratch. However, teaching Haskell in the bottom-up imperative way, with “Hello World” and calculator in ghci is counter productive. It leads right into the elephants vs clouds trap and it takes much longer to start appreciating the power and beauty of Haskell.

We need to start from Type Theory or even basic Category Theory. Make people understand and fall in love with Types, their structure, their daily lives, their relations with each other. A student has to become fluent in writing and understanding type level functions before they even think about writing “real” functions. It is in fact a pretty traveled road as well — if you have a reasonably strong math background, catch up on your Category Theory reading (Bartosz Milewski’s book is out and it’s excellent), study Typeclassopedia, use amazing Stephen Diehl’s What I Wish I knew when learning haskell as a reference, and get a feeling on how to structure real programs using monad transformer stacks, but whatever you do, under any circumstances, DO NOT read monad tutorials! Monads are very simple, it’s just another beautiful and powerful math abstraction, one among many. Yet, you will confuse and break your brain with false analogies if you read the tutorials. Study it in the context of Functor-Applicative-Monad hierarchy instead, so, again, Typeclassopedia.

Then, what was the point of all this? I am a very visual person when studying as are a lot of other people. So, I’ve been thinking for some time now that we can explain the math concepts underlying modern functional programming and type theory in a more accessible way using pictures and various cute animal related analogies. Existing literature on the subject is often too technical, even for practicing programmers, let alone kids, and focuses on formal proofs a lot.

Let us ease on trying to rigorously prove stuff. Let’s illustrate and explain those same concepts, from lambda calculus in SystemFw (even the name is a mouthful, but the machinery is again extremely simple!) to types and their relations to category theory and arrows, in a top-down visual approach. This may help people build new patterns, completely unrelated to imperative programming. Then they would gradually move to more technical, more in-depth study while already becoming proficient in writing elegant Haskell code for practical tasks. Something in the spirit of Learn you a Haskell but building up upon math foundation vs “hello world” and ghci REPL.

Hence, the idea of “Magical Haskell” — a book that tries to build a solid foundation for types, functional programming and Haskell specifically.

Would appreciate your thoughts and feedback!

--

--