如何限制类参数的角色?解决NewInt派生D类的编译错误
问题原因
GHC拒绝自动推导D NewInt实例,是因为类D的方法h中,类型参数a出现在f a的位置,而f是任意满足C约束的类型构造器。GHC必须考虑未来可能出现的C实例(比如那些类型参数为phantom角色、或关联了类型族的构造器),这些场景下f Int和f NewInt无法进行表示层面的强制转换,因此默认不允许通过GeneralizedNewtypeDeriving自动推导。
解决方案
方案1:手动实现实例
最直接的方式是手动编写D NewInt的实例,绕过自动推导的限制:
{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} class C (f :: Type -> Type) where g :: String -> f a class D a where h :: C f => a -> f a instance C Maybe where g _ = Nothing instance C (Either String) where g = Left instance D Int where h = g . show newtype NewInt = NewInt Int instance D NewInt where h (NewInt n) = h n
方案2:使用DeriveVia扩展推导
启用DeriveVia扩展,通过Int的D实例来推导NewInt的实例,这种方式更适合需要自动推导的场景:
{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DeriveVia #-} class C (f :: Type -> Type) where g :: String -> f a class D a where h :: C f => a -> f a instance C Maybe where g _ = Nothing instance C (Either String) where g = Left instance D Int where h = g . show newtype NewInt = NewInt Int deriving D via Int
方案3:添加类角色注解
如果能保证所有C的实例,其类型参数都具有Representational角色(即支持newtype的表示强制转换),可以给类D添加角色注解,让GHC允许自动推导:
{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE RoleAnnotations #-} type role D representational class C (f :: Type -> Type) where g :: String -> f a class D a where h :: C f => a -> f a instance C Maybe where g _ = Nothing instance C (Either String) where g = Left instance D Int where h = g . show newtype NewInt = NewInt Int deriving newtype D
注意:添加该注解意味着你承诺所有实现
C的类型构造器,其类型参数都支持表示层面的强制转换。如果未来出现不符合该承诺的C实例,可能会导致潜在问题。
内容的提问来源于stack exchange,提问作者Clinton
相关产品推荐
相关产品推荐

