Deep within the emerald canopy lies the foundation of all Haskell wisdom. Every value wears a type like armor, and the compiler stands guard at the forest gate, ensuring no ill-typed traveler passes through.
-- The forest speaks in types
data Tree a = Leaf
| Branch (Tree a) a (Tree a)
height :: Tree a -> Int
height Leaf = 0
height (Branch l _ r) =
1 + max (height l) (height r)
In this forest, algebraic data types grow like ancient oaks -- each branch a constructor, each leaf a base case. The traveler who masters pattern matching can navigate any path.
The elders of the forest convene as type classes -- contracts that any type may swear to uphold. To join the Eq council, a type must prove it can judge equality. To join Ord, it must know its place in the order of things.
class Describable a where
describe :: a -> String
instance Describable (Tree a) where
describe Leaf = "an empty clearing"
describe (Branch _ _ _) =
"a mighty branching oak"
At the lake's edge, reflections repeat into infinity. Here, functions call upon themselves -- each invocation a smaller reflection of the whole. The lake teaches that to solve a great problem, one must first solve its smaller self.
-- The lake reflects itself endlessly
fibonacci :: Integer -> Integer
fibonacci 0 = 0
fibonacci 1 = 1
fibonacci n = fibonacci (n - 1)
+ fibonacci (n - 2)
-- Folding the lake's depths
sumDepths :: [Int] -> Int
sumDepths [] = 0
sumDepths (x:xs) = x + sumDepths xs
But beware the endless reflection! Without a base case, the traveler descends forever. Wise adventurers carry foldr and foldl' as shields against unbounded recursion.
Beneath the surface, higher-order functions flow like underwater currents. map transforms every fish in the lake. filter lets only the worthy through. foldr gathers them all into a single treasure.
-- Currents that transform
enchant :: [String] -> [String]
enchant = map (++" the Brave")
. filter (not . null)
-- Gathering treasures
collect :: [Int] -> Int
collect = foldr (+) 0
Above the treeline, the air thins and abstractions soar. A Functor is a vessel that can be mapped over -- a box, a list, a possibility. At these heights, one learns to transform what lies inside a structure without breaking the structure itself.
-- Lifting functions to the peaks
class Functor f where
fmap :: (a -> b) -> f a -> f b
-- The Maybe peak: possibility
fmap (+1) (Just 5) -- Just 6
fmap (+1) Nothing -- Nothing
-- Applicative: combining peaks
liftA2 (+) (Just 3) (Just 4) -- Just 7
The Applicative peaks rise higher still, where functions themselves live inside structures and can be applied across the void between peaks.
Along the ridgeline, the traveler discovers Traversable -- the art of walking through a structure while carrying effects. Each step may succeed or fail, yet the journey collects all outcomes.
-- Walking the ridge with effects
validateQuest :: [String] -> Maybe [Int]
validateQuest = traverse readMaybe
-- Sequence: flipping layers
gather :: [Maybe a] -> Maybe [a]
gather = sequence
At last, the golden spires of the Monad Kingdom rise before you. Here dwells the most misunderstood power in all the land -- not a burrito, not a space suit, but simply: the ability to sequence computations that depend on previous results.
-- The Monad decree
class Applicative m => Monad m where
(>>=) :: m a -> (a -> m b) -> m b
-- A quest through the kingdom
questForTreasure :: IO ()
questForTreasure = do
name <- getLine
let greeting = "Hail, " ++ name
putStrLn greeting
putStrLn "The kingdom welcomes you!"
With do notation, the kingdom's scribes write sequential sagas where each line may depend on the last. IO, Maybe, Either -- all bend to the monad's decree.
In the inner court, monad transformers stack effects like layers of royal authority. ReaderT carries configuration, ExceptT handles failure, and StateT tracks mutable knowledge -- all woven together in a single computation.
-- The royal court of stacked effects
type Quest a = ReaderT Config
(ExceptT QuestError
(StateT Inventory IO)) a
runQuest :: Quest () -> IO ()
runQuest quest = do
result <- runStateT
(runExceptT
(runReaderT quest defaultConfig))
emptyInventory
case result of
(Left err, _) -> print err
(Right (), inv) ->
putStrLn "Quest complete!"