This passage, which unfortunately lacks references, about the development of ADTs in Haskell, from A History of Haskell: Being Lazy With Class, section 5.1:
Among corresponding to logical false (as stated in another answer), they are often used to create additional type constraints in combination with GADTs. For example:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE EmptyDataDecls #-}
import Data.List (groupBy)
data Zero
data Succ n
data Vec n a where
Nil :: Vec Zero a
Cons :: a -> Vec n a -> Vec (Succ n) a
vhead :: Vec (Succ n) a -> a
vhead (Cons v _) = v
vtail :: Vec (Succ n) a -> Vec n a
vtail (Cons _ v) = v
Here we have two such data types with no constructor. Their meaning here is just to represent natural numbers: Zero
, Succ Zero
, Succ (Succ Zero)
etc. They are used as phantom types in Vec
data type so that we can encode the length of a vector in its type. Then, we can write type-safe functions such as vhead
/vtail
that can be applied only to non-empty vectors.
See also [Haskell] Fixed-length vectors in Haskell, Part 1: Using GADTs where the example is elaborated in detail.