如何使用choosing?构造Lens' Foo Int类型透镜的问题探究
Haskell中构造Foo类型的Int透镜问题
数据类型定义
data SomeNestedData = SomeNestedData { sndValue :: Int } data OtherNestedData = OtherNestedData { ondValue :: Int } data Foo = Bar SomeNestedData | Baz OtherNestedData
需求
构造一个类型为Lens' Foo Int的透镜,使其能对任意foo :: Foo值进行Int类型值的获取与设置。
尝试实现及问题
尝试用choosing结合标准透镜、棱镜编写代码:
lensFooInt :: Lens' (Either Foo Foo) Int lensFooInt = (_Bar . _sndValue ) `choosing` (_Baz . ondValue)
但lensFooInt的类型是Lens' (Either Foo Foo) Int,不符合预期的Lens' Foo Int,由此提出两个问题:
- 如何有意义地使用
lensFooInt?Either类型似乎无法按预期运作。 - 是否存在实现
lensFooInt' :: Lens' Foo Int的方法?
解答
关于lensFooInt的使用
lensFooInt只能作用于Either Foo Foo类型的值,且要求左边的Foo必须是Bar构造的实例,右边的Foo必须是Baz构造的实例——只有这种情况下,棱镜_Bar和_Baz才能匹配成功,透镜才能正常获取或修改嵌套的Int值。如果左边是Baz或者右边是Bar,使用这个透镜会直接失败。它的适用场景非常狭窄,完全达不到处理任意Foo值的需求。
实现Lens' Foo Int的方法
存在,就是你补充给出的代码:
lensFooInt' = re _Right . ((_Bar . _sndValue ) `choosing` (_Baz . ondValue))
核心在于re _Right:_Right是一个针对Either a b的棱镜(Prism' (Either a b) b),re可以将棱镜反转成一个同构(Iso),把单个Foo值包装成Either Foo Foo的右分支。之后结合choosing组合的透镜,就能对任意Foo值生效:
- 当处理
Bar实例时,_Bar匹配成功,操作对应的sndValue; - 当处理
Baz实例时,_Baz匹配成功,操作对应的ondValue。
完全满足对任意Foo值进行Int获取与设置的需求。
内容的提问来源于stack exchange,提问作者ruben.moor
相关产品推荐
相关产品推荐

