能否用Lens/Prism从Haskell的C类型中获取非Maybe的a字段?
解决方案:手动定义Getter或Lens实现确定性访问
当然可以做到——虽然自动生成的Lens无法识别所有C构造器都能提取a字段的确定性,但我们可以手动定义Getter(只读)或Lens(可读写),直接返回Int而非Maybe Int,核心是利用我们对C类型结构的完整认知,把模式匹配逻辑封装到光学组件里。
1. 只读场景:定义Getter
如果只需要读取a字段的值,可以用Getter:
import Control.Lens data A = A { _aA :: Int } deriving (Show) data B = B { _aB :: Int } deriving (Show) data C = C1 A | C2 B deriving (Show) -- 定义Getter,直接从C实例提取Int cA :: Getter C Int cA = to $ \case C1 (A x) -> x C2 (B x) -> x -- 使用示例: -- let c = C1 (A 10) -- c ^. cA -- 返回 10 -- let c' = C2 (B 20) -- c' ^. cA -- 返回 20
2. 可读写场景:定义Lens
如果需要同时支持读取和修改a字段,可以定义Lens'(单目标Lens):
cALens :: Lens' C Int cALens f = \case C1 (A x) -> C1 . A <$> f x C2 (B x) -> C2 . B <$> f x -- 使用示例: -- 读取值:c ^. cALens -- 修改值:c & cALens .~ 100 -- 将c的a字段设为100 -- 增量修改:c & cALens %~ (+5) -- 将a字段加5
为什么自动生成不行?
像makeLenses这类自动工具只能基于单个类型的字段名生成光学组件,无法推断C的所有构造器都包含可提取a的子类型。但手动定义时,我们明确处理了C的所有分支,确保每个C实例都能对应唯一的Int,因此不需要用Maybe包裹结果。
这种手动定义的光学组件,本质就是把你原来的case表达式封装成了可组合的Lens/Getter,既能保留确定性访问的特性,又能享受Lens生态的组合能力。
内容的提问来源于stack exchange,提问作者RandomB
相关产品推荐
相关产品推荐

