It normally seems the following is illegal:
class Foo a where
foo :: a -> b -> a
Which makes sense; how do we know what b is?
However, if we look at Functor’s definition:
class Functor f where
fmap :: (a -> b) -> f a -> f b
we see a and b showing up even though we only specify f as a type variable. I’m guessing this is allowed because the compiler sees e.g. f a and can figure out that f itself must take an a, so it’s safe to use that a elsewhere in our Functor definition. Am I correct?
Let’s look at each line separately.
This declares a single-parameter type class called
Functor; the type which satisfies it will be calledf.Like any function definition, all the free type variables are implicitly
foralled—they can be replaced with anything. However, thanks to the first line,fis in scope. Thus,fmaphas the type signaturefmap :: forall a b. Functor f => (a -> b) -> f a -> f b. In other words, every functor needs to have a definition offmapwhich can work for anyaandb, andfmust have kind (the type of a type)* -> *; that is, it must be a type which takes another type, such as[]orMaybeorIO.What you said, then, is incorrect; the
aisn’t special, and if we had another function inFunctor, it wouldn’t see the sameaorb. However, the compiler does use thef abit to figure out what the kind offmust be. Additionally, yourFooclass is perfectly legal; I could specify an instance as followsThis satisfies
foo :: a -> b -> afor anyb; note that thebinFoo (a -> b)is different. Admittedly, it’s not a very interesting instance, but it’s perfectly legal.