Rich Hickey Doesn't Know Types
Clojure inventor Rich Hickey argues in one of his talks that:
a -> a
;[a] -> [a]
; It means nothing! It tells you nothing!
— Rich Hickey, Effective Programs.
If we take him to mean “a -> a
”, this is actually one of the simplest ways to
understand parametricity.
At first glance, it looks like a function of type a -> a
could do anything.
But parametricity rules that out. If you pass in a 1
, it can’t return 2
—
because the function must also work for any other type, not just numbers.
There’s no general “+1” operation across all types. Likewise, concatenation
only works for types with the right structure. In fact, the only lawful
implementation is the identity function:
id :: a -> a
id x = x
The signature also tells us there are no effects involved: there’s no IO
present, so the function can’t read from or write to the outside world.
Now consider [a] -> [a]
. Yes, technically there are infinitely many
implementations (a
, a ++ a
, a ++ a ++ a
, …), but they’re still highly
constrained. You can reverse a list, drop elements, take elements — but you
cannot inspect or manufacture new a
values. Parametricity tells us a
great deal.
A true shuffle would need a source of randomness, e.g.:
f :: Seed -> [a] -> [a] -- pass a seed
-- or
f :: [a] -> IO [a] -- obtain randomness in IO
This is the opposite of “nothing”: the signatures rule out far more than they permit, and those exclusions are exactly what make them informative.
As Kris Jenkins put it in Communicating in Types:
When you have a type error — and you always get a type error — the question is whether you get it from QA, or your users, or your compiler.
Type systems like Haskell’s aren’t a panacea, and there are some things that a language with a dynamic type system can do that are harder to achieve in Haskell (though what this means in the context of a business is a separate discussion). But dismissing these general types as uninformative overlooks the guarantees parametricity provides.