This is a continuation of @leftaroundabout's answer.
In Haskell, values have types. But types themselves also have types. When a type is acting as the type of another type, we call it a "kind".
The most important and common kind in Haskell is Type
, often denoted in signatures as *
. It is the kind of types that are "lifted", that is, whose values can be thunks that, when evaluated, can diverge, throw an error, etc... For example the type Int
has kind Type
.
There are also other types like Int#
that are not lifted. A value of type Int#
is never a thunk, it's always an actual value in memory.
In short, the in-memory representation of values is controlled by their type's kind.
RuntimeRep
is another kind. It is the kind of types like LiftedRep
and IntRep
. These types don't have any values, they exist merely to express things at the type level, as we shall see.
There is a super-magical type-level entity called TYPE
that, when parameterized with a type of kind RuntimeRep
(that is with a type that describes an in-memory representation) returns the kind of types whose values have that representation. For example, Type
is TYPE LiftedRep
, while the kind of Int#
is TYPE IntRep
.
ghci> :set -XMagicHash
ghci> import GHC.Prim
ghci> import GHC.Types
ghci> import Data.Kind
ghci> :kind (Int :: TYPE 'LiftedRep)
(Int :: TYPE 'LiftedRep) :: *
ghci> :kind Int#
Int# :: TYPE 'IntRep
Now we can get back to why undefined
has such a weird signature. The thing is, we want to be able to use undefined
in all functions, be it functions that return types of kind Type
, but also functions that return types of kind TYPE IntRep
(in other words: the Int#
type) or functions that return another unlifted type. Otherwise, we would need multiple different versions of undefined
, which would be annoying.
The solution is to make undefined
levity-polymorphic. The signature says: for any possible in-memory representation (RuntimeRep
) and for any possible type whose values have that representation, undefined
counts a member of that type.
undefined
is not a function. But if you're still a beginner, I'd just pretend the type isundefined :: a
. The rest is advanced magic. – Skindeepundefined
is not a function at the language (user) level. However, due to the class constraintHasCallStack =>
it is implemented as a function internally. – Skindeepundefined
or Java'snull
, you probably want to useData.Maybe
. A good introduction is learnyouahaskell.com/modules – Thyrotoxicosis