如何使用Traversal移除列表中特定值构造器(保留Banana)
解决方案
步骤1:定义针对Banana的Traversal
你可以选择手动定义Traversal,也可以用TemplateHaskell自动生成Prism(Prism属于Traversal的子类,完全满足需求)。
手动定义Traversal
import Control.Lens data Fruit = Apple Int | Banana Color Int deriving (Show) data Color = Red | Green deriving (Show) -- 仅聚焦Banana构造器的Traversal _Banana :: Traversal' Fruit Fruit _Banana f banana@(Banana _ _) = f banana _Banana f apple@(Apple _) = pure apple
自动生成Prism(更简洁)
启用TemplateHaskell后,工具会自动为Fruit的每个构造器生成对应的Prism:
{-# LANGUAGE TemplateHaskell #-} import Control.Lens data Fruit = Apple Int | Banana Color Int deriving (Show) data Color = Red | Green deriving (Show) -- 自动生成_Apple和_Banana两个Prism makePrisms ''Fruit
步骤2:移除Apple,仅保留Banana
利用^..(toListOf的运算符形式)提取所有匹配_Banana的元素,即可得到仅含Banana的列表:
let basket = [Apple 3,Banana Red 30,Apple 6] onlyBananas :: [Fruit] onlyBananas = basket ^.. traverse . _Banana -- 结果:[Banana Red 30]
额外:用该Traversal执行修改/设置操作
这个Traversal完全支持你提到的修改、设置操作:
修改操作(over)
给所有Banana的数值加10:
-- 方式1:直接修改整个Banana构造器 updatedBananas :: [Fruit] updatedBananas = over (traverse . _Banana) (\(Banana c n) -> Banana c (n + 10)) basket -- 结果:[Apple 3, Banana Red 40, Apple 6] -- 方式2:聚焦到Banana的第二个字段(更简洁) updatedBananas' :: [Fruit] updatedBananas' = over (traverse . _Banana . _2) (+10) basket
设置操作(set)
把所有Banana的颜色设为Green:
greenBananas :: [Fruit] greenBananas = set (traverse . _Banana . _1) Green basket -- 结果:[Apple 3, Banana Green 30, Apple 6]
内容的提问来源于stack exchange,提问作者Shawn Zhang
相关产品推荐
相关产品推荐

