 <p>You can do it with Generalized Algebraic Data Types. We can create a generic (GADT) type with data constructors that have type constraints. Then we can define specialized types (type aliases) that specifies the full type and thus limiting which constructors are allowed.</p> <pre><code>{-# LANGUAGE GADTs #-} data Zero data Succ a data Axis a where X :: Axis (Succ a) Y :: Axis (Succ (Succ a)) Z :: Axis (Succ (Succ (Succ a))) type Axis2D = Axis (Succ (Succ Zero)) type Axis3D = Axis (Succ (Succ (Succ Zero))) </code></pre> <p>Now, you are guaranteed to only have <code>X</code> and <code>Y</code> passed into a function that is defined to take an argument of <code>Axis2D</code>. The constructor <code>Z</code> fails to match the type of <code>Axis2D</code>.</p> <p>Unfortunately, GADTs do not support automatic <code>deriving</code>, so you will need to provide your own instances, such as:</p> <pre><code>instance Show (Axis a) where show X = "X" show Y = "Y" show Z = "Z" instance Eq (Axis a) where X == X = True Y == Y = True Z == Z = True _ == _ = False </code></pre>