Haskell中Constant类型的两个Functor实例为何类型检查结果不同?
Constant have different type-checking results? Let's break down exactly why one instance works and the other throws a type error, starting with core Haskell type system constraints.
First, let's recap the definitions and signatures we're working with:
- The newtype:
newtype Constant a b = Constant { getConstant :: a } deriving (Eq, Show) - The Functor type class requires
fmap :: (b -> c) -> f b -> f c. ForFunctor (Constant a), this specializes to:fmap :: (b -> c) -> Constant a b -> Constant a c
Why the first instance works
The first implementation uses pattern matching to deconstruct and reconstruct the Constant value:
instance Functor (Constant a) where fmap _ (Constant x) = Constant x
Here's what happens during type checking:
- We pattern match on
Constant x, wherexhas typea(sincegetConstantreturnsa). - When we reconstruct
Constant x, the constructorConstanthas the polymorphic typea -> Constant a c—it can produce aConstantvalue for any typec(the second type parameter of the newtype). - The type system infers that the returned
Constant xshould have typeConstant a c(matching thefmapsignature's return type), which is perfectly valid because the constructor lets us pick anycwe need. We're not reusing the input value directly—we're building a logically identical value with the correct type parameter, so the inputConstant a band outputConstant a cplay nicely together.
Why the second instance fails
The second implementation tries to return the input value directly:
instance Functor (Constant a) where fmap _ x = x
This runs into a type mismatch because:
- The input
xhas typeConstant a b(from thefmapsignature's second parameter). - The
fmapsignature requires returning a value of typeConstant a c. - For
xto be valid as the return value, the type system would needConstant a bto equalConstant a c—meaningb ~ c(b must be the same type as c). But this violatesfmap's polymorphic contract:fmapis supposed to work for any typesbandc, not just when they're identical.
GHC can't reconcile this unspoken constraint, so it throws an error saying it expected Constant a b but got Constant a a1 (or similar)—because it can't unify the two distinct type variables for the newtype's phantom second parameter.
Key takeaway
Even though Constant a b and Constant a c share the same runtime representation (since the second type parameter is phantom), Haskell's type system treats them as distinct types. Pattern matching and reconstructing lets you leverage the constructor's polymorphism to produce a value with the correct type parameter. Returning the input directly traps you with the original type parameter, which doesn't match what fmap promises to return.
内容的提问来源于stack exchange,提问作者Timo Denk

