Haskell 使用透镜应用依赖于多个场的函数
设Haskell 使用透镜应用依赖于多个场的函数,haskell,types,haskell-lens,Haskell,Types,Haskell Lens,设A,B,Cbe类型,有两个函数f:(A,B)->A和g:(A,B)->B。考虑以下记录类型< /P> data Rec=Rec{{u a::a,{u b::b,{u c::c} 使用透镜组合器定义将(Rec a b c)映射到(Rec(f a b)(g a b)c)的函数最优雅的方式是什么 镜头 镜头a、b和c将按照fmap(中缀翻转fmap)手工书写,如下所示: 结合来自的&&&,我们可以优雅地编写所需的函数 inAB :: ((A, B) -> A) -> ((A, B) -&
A,B,C
be类型,有两个函数f:(A,B)->A
和g:(A,B)->B
。考虑以下记录类型< /P>
data Rec=Rec{{u a::a,{u b::b,{u c::c}
使用透镜
组合器定义将(Rec a b c)
映射到(Rec(f a b)(g a b)c)
的函数最优雅的方式是什么 镜头
镜头a
、b
和c
将按照fmap
(中缀翻转fmap)手工书写,如下所示:
结合来自的&&&
,我们可以优雅地编写所需的函数
inAB :: ((A, B) -> A) -> ((A, B) -> B) -> Rec -> Rec
inAB f g = ab %~ (f &&& g)
如果您对镜头库非常满意,您可能更喜欢使用(ab%~(f&&g))
而不是inAB f g
有一个从透镜a
和b
构建透镜ab
的功能,因为通常情况下,两个透镜在同一底层结构上的乘积不是产品在一个底层结构上的透镜;两个透镜都可能试图改变相同的基本场,违反透镜定律
没有镜片
如果没有镜头,您可以定义一个函数,将函数应用于记录的\u a
和\u b
字段
onAB :: (A -> B -> c) -> Rec -> c
onAB f r = f (_a r) (_b r)
基于每个字段的函数修改\u A
和\u b
字段的函数只是将\u A
和\u b
设置为应用于字段的两个函数的结果
inAB' :: (A -> B -> A) -> (A -> B -> B) -> Rec -> Rec
inAB' f g r = r {_a = onAB f r, _b = onAB g r}
加入几杯咖喱,我们就可以得到你想要的签名了
inAB :: ((A, B) -> A) -> ((A, B) -> B) -> Rec -> Rec
inAB f g = inAB' (curry f) (curry g)
较慢、不太优雅的镜头
对于镜头,我们也可以说我们是set
inga
和b
。它并不比使用记录构造函数更优雅,而且需要构造两次记录
inAB' :: (A -> B -> A) -> (A -> B -> B) -> Rec -> Rec
inAB' f g r = set b (onAB g r) . set a (onAB f r) $ r
为什么需要镜头组合器呢?两个问题讨论了你在最后提到的问题:。在这种特定情况下,相关镜头是合法的,因此原则上可以从头开始定义它,尽管这可能不值得费心。手动编写也没那么糟糕:
\u ab f(Rec a b c)=f(a,b)\(a',b')->Rec a'b'c
。那么这个函数就是\u ab%~\(a,b)->(fab,gab)
@cchalmers,实际上不是;我的“不值得麻烦”只是相对于无镜头的解决方案,并且假设合成镜头在其他地方不会被证明有用。
inAB :: ((A, B) -> A) -> ((A, B) -> B) -> Rec -> Rec
inAB f g = inAB' (curry f) (curry g)
inAB' :: (A -> B -> A) -> (A -> B -> B) -> Rec -> Rec
inAB' f g r = set b (onAB g r) . set a (onAB f r) $ r