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

Haskell中基于现有类实例派生其他类型类实例的简化方案咨询

最优解决方案:使用DerivingVia扩展

这是GHC 8.6版本开始提供的标准扩展,专门用于newtype类型的实例复用,不需要TemplateHaskell,也不需要开启全局通用实例带来的冲突风险。

实现步骤

  1. 在文件头部开启DerivingVia扩展:
{-# LANGUAGE DerivingVia #-}
  1. 定义通用的包装类型,为它实现所有需要复用的类型类实例:
-- 通用包装类型,所有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
  1. 自定义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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 07:06:04