如何在不耦合依赖的前提下递归推导newtype的C类实例?
解决方案:在不耦合P与X的前提下实现C B实例
问题背景
- 包P定义类型:
newtype A = A Int,且P不依赖外部包X - 外部包X定义类
C及实例instance C Int where ... - 包Q定义类型
newtype B = B A,需要实现instance C B where ... - 现有方案均有弊端:要么耦合P与X,要么易受A的底层类型变更影响,要么代码结构冗余
可行方案1:手动拆解构造器实现实例
在包Q中直接实现C B实例,通过拆解B和A的构造器,复用X中已有的C Int实例:
-- 包Q中代码,需导入P的A类型和X的C类 import P (A(..)) import X (C(..)) instance C B where -- 替换成C类实际的方法名和逻辑,这里以类方法`cMethod`为例 cMethod (B (A underlyingVal)) = cMethod underlyingVal
优势:
- P完全不需要依赖X,彻底解耦
- 只要A的构造器保持公开(或Q有权限访问),即使A的底层类型变更(如改为
Integer),只要X提供了对应类型的C实例,仅需调整拆解时的类型匹配逻辑,不会彻底破坏代码
可行方案2:利用Coercible自动转换实现实例
借助Haskell的Coercible类型类(newtype默认自动拥有Coercible实例),通过coerce函数直接复用底层类型的C实例,无需手动拆解构造器:
-- 包Q中代码,需启用Coercible相关扩展 {-# LANGUAGE FlexibleContexts #-} import Data.Coerce import P (A) import X (C(..)) newtype B = B A instance C B where -- 直接coerce底层类型的C方法到B上,无需关心A的具体封装 cMethod = coerce (cMethod :: Int -> ...) -- 替换为C类方法的实际类型签名
如果启用GeneralizedNewtypeDeriving扩展,甚至可以更简洁,但需要确保Coercible B Int成立(由于Coercible的传递性,B→A→Int的转换是自动的):
{-# LANGUAGE GeneralizedNewtypeDeriving #-} import P (A) import X (C) newtype B = B A deriving newtype (C)
注:第二种写法需要GHC支持GeneralizedNewtypeDeriving,且要求A的底层类型(如Int)有C实例,同时B与底层类型是Coercible的——由于newtype的特性,这个条件天然满足。
优势:
- 代码极简,无需手动处理构造器拆解
- 当A的底层类型变更时,只要X提供了新底层类型的
C实例,代码无需修改(Coercible会自动处理新的类型转换) - 依然保持P与X的完全解耦
内容的提问来源于stack exchange,提问作者Clinton
相关产品推荐
相关产品推荐

