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

如何使用generic-lens为类型编写Plated实例?

使用generic-lens实现Plated类型实例

当然可以用generic-lens库实现Plated类型实例,lens生态里也有对应的方案替代optics的gplate。

问题分析

你之前用types遇到的问题,是因为types是Biplate的泛型实现——当源类型s和目标类型a相同时,它会匹配整个根节点(表现等同于id),而Plated的plate需要仅遍历当前类型的直接子节点,不包含根节点本身。

解决方案:用genericTraversal实现

generic-lens提供了genericTraversal,它专门用于生成遍历同类型子节点的Traversal',完全符合Plated的需求。

完整代码示例

import Control.Lens.Plated (Plated(plate), everywhere)
import Data.Generics.Traversal (genericTraversal)
import GHC.Generics (Generic)

-- 定义Expr类型并派生Generic
data Expr = Lit Int | Add Expr Expr deriving (Generic, Show)

-- 实现Plated实例
instance Plated Expr where
    plate = genericTraversal

-- 测试:将所有Lit节点的值加1
addOne :: Expr -> Expr
addOne = everywhere $ \case
    Lit n -> Lit (n + 1)
    e     -> e

-- 测试用例
main :: IO ()
main = print $ addOne (Add (Lit 1) (Add (Lit 2) (Lit 3)))
-- 输出:Add (Lit 2) (Add (Lit 3) (Lit 4))

替代方案:手动构造Traversal

如果你不想依赖genericTraversal,也可以用generic-lens的构造器和类型遍历手动实现,更精细地控制子节点的遍历:

import Control.Lens.Plated (Plated(plate))
import Data.Generics.Product (types)
import Data.Generics.Sum (_Ctor)
import Control.Lens (Traversal', failing)
import GHC.Generics (Generic)

data Expr = Lit Int | Add Expr Expr deriving (Generic, Show)

instance Plated Expr where
    plate = failing
        -- Lit没有子Expr节点,返回空遍历
        (_Ctor @"Lit" . traverse)
        -- Add有两个Expr子节点,用types遍历它们
        (_Ctor @"Add" . types @Expr)

额外说明

在lens生态中,还有专门的generic-plated包可以自动生成Plated实例,但如果你已经在使用generic-lens,上面的方案更贴合现有依赖栈。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 10:22:42