You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何限制类参数的角色?解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 02:20:48