Haskell中基于现有类实例派生其他类型类实例的简化方案咨询
最优解决方案:使用DerivingVia扩展
这是GHC 8.6版本开始提供的标准扩展,专门用于newtype类型的实例复用,不需要TemplateHaskell,也不需要开启全局通用实例带来的冲突风险。
实现步骤
- 在文件头部开启
DerivingVia扩展:
{-# LANGUAGE DerivingVia #-}
- 定义通用的包装类型,为它实现所有需要复用的类型类实例:
-- 通用包装类型,所有Narrow类型的实例都可以通过这个类型派生 newtype Narrowed a = Narrowed a deriving (Eq, Ord, Bounded) via a -- 为Narrowed实现通用Random实例 instance (Narrow a, Ord a, Bounded a, SR.Random (BaseType a)) => SR.Random (Narrowed a) where randomR (Narrowed lo, Narrowed hi) g = let (res, g') = randomR (lo, hi) g in (Narrowed res, g') random g = let (res, g') = random g in (Narrowed res, g') -- 可以继续为Narrowed实现其他需要复用的类型类,比如Num: instance (Narrow a, Num (BaseType a)) => Num (Narrowed a) where (Narrowed x) + (Narrowed y) = Narrowed $ bless $ wide x + wide y (Narrowed x) * (Narrowed y) = Narrowed $ bless $ wide x * wide y abs (Narrowed x) = Narrowed $ bless $ abs $ wide x signum (Narrowed x) = Narrowed $ bless $ signum $ wide x fromInteger n = Narrowed $ bless $ fromInteger n negate (Narrowed x) = Narrowed $ bless $ negate $ wide x
- 自定义newtype时,直接通过
deriving via语法批量派生实例:
newtype UIDouble = UIDouble Double deriving (Eq, Ord) -- 一行完成所有需要的类型类派生 deriving (SR.Random, Num, Bounded) via (Narrowed UIDouble) instance Bounded UIDouble where minBound = UIDouble 0 maxBound = UIDouble 1 instance Narrow UIDouble where type BaseType UIDouble = Double bless = UIDouble wide (UIDouble x) = x
可选方案:可控使用UndecidableInstances
你之前尝试的通用实例写法是可行的,UndecidableInstances的风险主要是可能导致编译器实例搜索进入无限循环,以及可能和其他库的实例产生冲突。在你这个场景下,只要你能保证所有实现了Narrow类的类型不会再有其他同类型类的实例定义,就可以安全使用,不会有运行时风险。
内容的提问来源于stack exchange,提问作者mhwombat
相关产品推荐
相关产品推荐

