QUEST

haskell

haskell.quest scroll to begin

II / type hierarchy

The language thinks in shapes. Circles for type classes. Triangles for instances. Rectangles for the data they carry. Lines trace the relationships -- a living diagram of what compiles and what does not.

type class -- a contract between types
instance -- proof a type honors that contract
data type -- a structure carrying values

CHAPTER ONE

First, the function arrow

Haskell is hard. We will not pretend otherwise. What we will do is move slowly, and let each idea earn its place before the next arrives. The first idea is the function arrow.

Every Haskell function takes one argument and returns one value. Two arguments? That is a function returning a function. Three arguments? A function returning a function returning a function. The arrow is right-associative, and that is not an accident -- it is the entire shape of the language laid bare in a single piece of syntax.

define.hs

Read add :: Int -> Int -> Int as add takes an Int, and returns a function from Int to Int. That parsing is not pedantry. It is what makes addOne = add 1 compile without ceremony. Currying is not a feature added later. It is the floor everything else stands on.

Types describe shape, not behavior

A type signature in Haskell is a promise about shape. map :: (a -> b) -> [a] -> [b] says: give me a way to turn an a into a b, and a list of a, and I will give you a list of b. The signature does not say how. It does not have to. The shape is enough constraint that the implementation is almost forced.

map.hs

The lowercase a and b are type variables -- they stand for any type at all. The function does not know what they are, and that ignorance is the source of its strength. A function that can work for every type can only do one thing: rearrange structure. It cannot inspect, cannot decide, cannot leak.

Why this matters before monads

The instinct, when starting Haskell, is to rush to monads. Resist it. Monads only make sense once you have lived with the function arrow, with currying, with parametric polymorphism, long enough that they feel ordinary. We will get there. The route is shorter than the rumor suggests, but only if we walk it in order.

maybe.hs

safeHead is the smallest honest function you can write. It admits, in its type, that it might not have an answer. The list could be empty. The compiler will force you, later, to decide what to do in that case -- and that forcing is the whole point. Haskell will not let you forget.

The road from here

Beyond this chapter sit type classes, then functors, then applicatives, then monads. Each one is a small re-reading of what you already know. None of them are fundamentally new -- they are all variations on shape preserved through transformation. By the time you arrive at the word monad, it will look ordinary. That is the goal of this quest.

  1. 01Functions, currying, parametric types
  2. 02Algebraic data types and pattern matching
  3. 03Type classes and lawful instances
  4. 04Functor, Applicative, Monad as one idea three times
  5. 05IO, purity, and the boundary at the edge of the program
  6. 06Type-level programming -- when types themselves compute

Take this slowly. Re-read. Type the examples by hand into a file and run them. There is no path to fluency in any language that skips the slow work of actually writing it. Haskell is no exception, and would be insulted by the comparison.

IV / the skyline of understanding

Each building is a milestone on the road. Their windows light as you pass them. Look back when you reach the end -- the city you crossed will be glowing.

  1. 01 Hello, types your first signature
  2. 02 Pattern matching deconstruction by shape
  3. 03 Algebraic data sums, products, recursion
  4. 04 Type classes contracts among types
  5. 05 Functor map over a context
  6. 06 Applicative apply within a context
  7. 07 Monad sequence and bind
  8. 08 IO the outside world, isolated
  9. 09 Type-level the language inside the language