Functors
haskell1-8 closed Course 1 with typeclasses. This chapter opens Course 2 with the first of three — Functor, then Applicative, then Monad — building toward the single biggest payoff in this entire track.
The Functor Typeclass
A real typeclass, same shape as haskell1-8's own Eq/Ord/Show — but with a genuine new wrinkle: f here isn't a concrete type, it's a type constructor — something that itself takes a type parameter, like Maybe or []. Functor is a capability for "things that hold a type parameter," not for ordinary types directly.
"A Box You Can Map Over" — The Core Intuition
Maybe, [], and other "container-like" types let a function be applied to whatever's inside, without unwrapping and rewrapping by hand. This is genuinely one step further than java2-1's/csharp2-1's own generics — a generic type stores a T; a Functor additionally supplies a uniform way to transform whatever it's holding, without needing to know anything about the container itself.
fmap vs. map — A Real, Honest Naming Wrinkle
map is Haskell's own older, list-specific function, predating the generalized Functor typeclass. fmap is the generalized version, working for any Functor, not just lists. A real, slightly awkward wart from decades of language evolution: map still exists separately, mostly for historical and beginner-friendliness reasons, and newcomers often reasonably ask why there are two. Worth naming honestly rather than glossing over.
The <$> Operator — fmap's Infix Alias
<$> is fmap's infix alias, seen constantly in real Haskell code from here on — worth introducing now rather than later.
Writing a Functor Instance for Your Own Type
Functor isn't limited to Maybe and [] — any appropriately-shaped type can genuinely participate. This instance reuses haskell1-6's own Tree a directly, applying f to every value while leaving the tree's own shape untouched.
The Functor Laws
A real, honest limitation: these two laws — mapping id changes nothing, and mapping a composed function equals composing the mapped functions — are not enforced by the compiler at all. They're purely a convention instance authors are expected to uphold, the same honest-acknowledgment spirit as Course 1's own record-syntax field-collision wart.
| Aspect | Java/C# generics (java2-1, csharp2-1) | Haskell Functor |
|---|---|---|
| What it provides | a container that holds a T | a container PLUS a uniform way to transform what's inside |
| Requires knowing the container's internals | often, to transform contents | never — fmap works the same regardless |
| Compiler-enforced correctness | type-safety only | type-safety only — the Functor laws are unenforced |
<$> is the idiomatic form seen constantly in real Haskell — fmap itself is more useful for explanation and teaching contexts, exactly the balance this chapter uses.
Functor instance (one that doesn't actually satisfy fmap id = id) compiles and runs without any warning at all, and can produce genuinely surprising, wrong behavior anywhere code assumes the laws hold — including, later, inside do-notation. There is no compiler safety net here.
Coding Challenges
Use fmap to double every value inside a Just 21, a Nothing, and a list [1,2,3,4], printing all three results.
📄 View solutionUsing haskell1-6's own Tree a data type, write a Functor instance for it, build a small sample tree, apply fmap (*10) to it, and print the result.
📄 View solutionWrite a short comment explaining why f in "class Functor f where" must be a type constructor (like Maybe) rather than a concrete type (like Int), tying your answer to what fmap's own type signature requires.
📄 View solutionChapter 1 Quick Reference
- Functor's f is a type constructor, not a concrete type — a genuine new wrinkle beyond ordinary typeclasses
- fmap applies a function to whatever's inside a container without unwrapping/rewrapping by hand
- map is Haskell's older, list-only function; fmap is the generalized version for any Functor — a real historical wart, honestly named
- <$> is fmap's infix alias, the idiomatic form in real code
- Any appropriately-shaped type can get a Functor instance, including haskell1-6's own Tree a
- The Functor laws (fmap id = id, etc.) are pure convention — never compiler-enforced
- Next chapter: Applicatives — combining independent computations, the bridge to Chapter 3's own monad reveal