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

存在类型族时无需手动编写约束派生Show实例的实现方法

核心实现方案

利用GHC的DerivingVia扩展配合泛型派生+量化约束,一次性解决所有同结构ADT的实例派生问题,无需为每个字段手动写约束。


1. 开启必要扩展

{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FlexibleContexts #-}

2. 定义通用约束同义词

针对你的Switchable类型族定义通用约束,一次性覆盖所有字段的约束要求:

import GHC.Generics
import Data.Kind
import Prelude

type family Switchable (f :: Type -> Type) x where
  Switchable TextName x = String
  Switchable Same x = x
data TextName x
data Same x

-- 通用约束:对于所有满足c的类型x,Switchable f x也满足c
type AllSwitchable c f = forall x. c x => c (Switchable f x)

3. ADT定义与自动派生

ADT仅需要派生Generic,然后通过Generically包装器派生你需要的Show/Eq等实例,无需手动写任何字段约束:

data Foo f = Foo
  { fooField :: Switchable f Int
  , fooField2 :: Switchable f Bool
  -- 新增任意Switchable字段都不需要修改派生逻辑
  } deriving Generic
  deriving (Show, Eq) via Generically (Foo f)

4. 为标签类型预制约束实例(可选,进一步简化调用)

针对你使用的TextName和Same标签预制实例,后续使用时无需携带任何额外约束:

-- TextName场景下所有Switchable结果都是String,天然满足Show/Eq等类
instance AllSwitchable Show TextName
instance AllSwitchable Eq TextName

-- Same场景下Switchable结果等于原类型,约束直接透传
instance AllSwitchable Show Same
instance AllSwitchable Eq Same

效果验证

直接使用无需额外声明约束:

main = do
  print $ Foo @TextName 123 True -- 输出Foo {fooField = "123", fooField2 = "True"}
  print $ Foo @Same 123 True -- 输出Foo {fooField = 123, fooField2 = True}

内容的提问来源于stack exchange,提问作者tom

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 04:15:02