last :: [a] -> a
is the Haskell98 syntax for the System F type
last :: ∀ a. [a] -> a
The ∀ a
can be understood as a kind of type-level lambda binding, i.e. before the actual value level list parameter the function accepts a type-level argument telling what the type of elements contained in the list. This universal quantification makes the function parametrically polymorphic.
Normally, the type variables are automatically inserted by the type checker. In newer GHC Haskell, you can also explicitly apply them:
Prelude> :set -XTypeApplications
Prelude> :t last @Int
last @Int :: [Int] -> Int
Prelude> last @Double [5,6,7]
7.0
negate
is also parametrically polymorphic, but unlike last
it does not work truely “for all” types, but only for those that have a Num
instance (which both Int
and Double
do, but not e.g. Char
). In other words, it accepts not only an extra argument specifying the type, but also a proof that it does indeed have a Num
instance. That too will be inserted automatically by the compiler.
negate :: ∀ a. Num a => a -> a
Prelude> :t negate @Int
negate @Int :: Int -> Int
Prelude> :t negate @Char
<interactive>:1:1: error:
No instance for (Num Char) arising from a use of ‘negate’
Num a
is a "constraint",Num
is a "type class" / "typeclass",a -> a
is a "parametrically-polymorphic type",a
is a "type variable" ( the "parameter" in the "parametrically-polymorphic" ).[b] -> b
is a parametrically-polymorphic type,[b]
is a parametrically-polymorphic type,b
is a type variable. – Wenn