haskell.quest
Your quest begins here.
Values
Every quest begins with the smallest piece -- a value. In Haskell, a value is a fact carved into the world: three is three, true is true, "hello" is "hello". Values are immutable. Once you say x = 7, that x will be 7 forevermore in its corner of the universe. There is no later mutation, no surprise reassignment. This honesty is the foundation everything else stands on.
Immutability isn't a restriction -- it's a promise. A value, once named, will never lie to you.
-- A value is a name bound to an expression
greeting = "Welcome, traveler."
torches = 3
isOpen = True
-- Values can be combined into new values
message = greeting ++ " " ++ show torches
Functions
Beyond the village lies the Forest of Functions -- a workshop of transformation. A function takes something in, weaves its rule, and offers something new. In Haskell, functions are first-class citizens of the realm: you can pass them to other functions, return them, store them, name them. They are also pure: same input, same output, every time. No hidden side, no quiet effect.
Functions are simple machines. Combine them and you can build anything -- without ever surprising yourself.
-- A function: input becomes output, no surprises
double x = x * 2
-- Pattern matching shapes the input
greet "friend" = "Welcome home."
greet name = "Hail, " ++ name ++ "!"
-- Functions compose like rivers joining
shout = map toUpper . greet
Types
The Fortress of Types stands between you and chaos. Each gate is labeled: Int, String, Bool, [a], Maybe a. Types are not your jailer -- they are the cartographer of what is possible. The compiler reads your types like a watchful guide, refusing the doors that don't fit. A "type error" is not an insult; it is the world telling you, kindly, that this path was never going to lead where you wanted.
A well-typed program cannot go horribly wrong. Lean into the types -- they are an oracle, not a prison.
-- Type signatures spell out the shape of things
torches :: Int
torches = 3
double :: Int -> Int
double x = x * 2
-- Custom types describe your world precisely
data Door = Open | Locked String
data Item = Torch | Map | Key String
Higher-Order
Climb the Tower of Higher-Order Functions. Here, functions accept other functions as arguments and return them as results. map takes a function and a list, giving back a list of transformed things. filter takes a question and gives back the items that answer "yes." foldr takes a way to combine and a starting place. With these three you can shape almost any data the realm has to offer.
A function is a value. When you can pass behavior the way you pass numbers, the world becomes much smaller -- and much more powerful.
-- map: apply a function to every element
map double [1,2,3] -- => [2,4,6]
-- filter: keep elements that pass a test
filter even [1..10] -- => [2,4,6,8,10]
-- foldr: collapse a list with a combining function
foldr (+) 0 [1,2,3,4] -- => 10
Typeclasses
The Guild Hall is where types swear their oaths. A typeclass is a contract: any type that joins the Eq guild must know how to be compared. Any type in Ord can be ordered. Any in Show can be displayed. Typeclasses let you write a single function that works for many types -- not because it ignores their differences, but because each type pledged the same skill.
Polymorphism through promises. A typeclass says "all who join can do this" -- and the compiler keeps everyone honest.
-- A typeclass: a contract types may sign
class Greetable a where
greeting :: a -> String
-- Instances: types fulfilling the contract
instance Greetable String where
greeting s = "Hail, " ++ s
instance Greetable Int where
greeting n = "Welcome, traveler #" ++ show n
Monads
Within the nested walls of the Citadel sits the most whispered-about creature: the Monad. Forget the legends. A monad is a pattern for sequencing -- a way to thread context (a maybe-failure, a list of possibilities, the outside world) through a computation without letting it leak everywhere. do-notation is the spell that makes monadic code read like a story: do this, then this, then this -- the context follows along, quietly handled.
Monads are not mysterious. They are how Haskell tells stories where each step depends on the last -- without breaking its purity vow.
-- Maybe carries the possibility of nothing
findKey :: String -> Maybe Item
findKey name = ...
-- do-notation: sequence steps that may fail
openVault = do
key <- findKey "vault"
map <- findKey "map"
torch <- findKey "torch"
return (key, map, torch)
The Treasure
A program that uses every stage of your journey.
01-- Stage 3: Types describe the shape of our world
02data Item = Torch | Map | Key String deriving (Show, Eq)
03data Hero = Hero { name :: String, inventory :: [Item] }
04
05-- Stage 1: Values -- our starting hero
06apprentice = Hero "Alia" [Map, Torch]
07
08-- Stage 2: Functions transform values into new values
09collect :: Item -> Hero -> Hero
10collect item h = h { inventory = item : inventory h }
11
12-- Stage 4: Higher-order functions reshape inventories
13torchesHeld :: Hero -> Int
14torchesHeld = length . filter (== Torch) . inventory
15
16-- Stage 5: Typeclasses give types shared abilities
17instance Show Hero where
18 show h = name h ++ " carries " ++ show (inventory h)
19
20-- Stage 6: Monads sequence the journey, possibility and all
21enterCitadel :: Hero -> Maybe Hero
22enterCitadel h = do
23 _ <- find (== Map) (inventory h)
24 _ <- find (== Torch) (inventory h)
25 return (collect (Key "Citadel") h)
Hover any line to see which stage it belongs to.