为何Haskell中用()表示空约束报错?GHC 8.2.2技术问询
() Can't Represent an Empty Constraint (And How to Fix It) Great question—let's unpack why your initial ConstraintKinds attempt failed, and clarify how to properly use () as an empty constraint in Haskell.
The Core Kind Mismatch
Your error boils down to a fundamental kind mismatch:
- Out of the box,
()is a unit type with kindType(or*in older GHC lingo). It represents the single value(). - When you enable
ConstraintKinds,()gains a second identity: it can act as an empty constraint with kindConstraint—this is a constraint that's always satisfied, with no requirements on the type it's applied to.
But here's the problem: your code tried to force () to have the kind Type -> Constraint, which is a function kind (takes a type, returns a constraint). () is a 0-ary construct—it doesn't take any arguments—so this kind annotation is invalid. GHC's error message is telling you exactly that: it expected something with kind * -> Constraint, but () only has kind *.
Fixing the ConstraintKinds Approach
To create an empty constraint that works for any type (i.e., has kind Type -> Constraint), you need to define a type synonym that accepts a type parameter and returns the empty () constraint:
{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE KindSignatures #-} import Data.Kind (Type, Constraint) -- Explicitly declare the kind of Empty (optional but clear) type Empty :: Type -> Constraint type Empty a = () -- For any type 'a', the constraint is just the empty '()' main :: IO () main = return ()
This works because:
Emptynow correctly has kindType -> Constraint: it takes a typeaand returns the empty constraint().- With
ConstraintKindsenabled,()is a valid constraint (kindConstraint) that imposes no conditions ona—every type satisfies it.
How This Compares to Your FlexibleInstances Solution
Your existing class-based approach is another perfectly valid way to make an empty constraint:
{-# LANGUAGE FlexibleInstances #-} class Empty x instance Empty x
This works by defining a class with no methods, then using FlexibleInstances to create a "catch-all" instance that applies to every possible type x. Semantically, it's identical to the ConstraintKinds approach—both represent a constraint that's always satisfied—but they use different parts of Haskell's type system:
- The class approach leans on Haskell's typeclass mechanism, requiring
FlexibleInstancesfor the universal instance. - The
ConstraintKindsapproach uses GHC's built-in empty constraint(), which is more concise and avoids needing an extra class/instance pair.
内容的提问来源于stack exchange,提问作者libeako

