使用镜头应用依赖于多个场的功能

Kat*_* J. 7 haskell types haskell-lens

我们A, B, C是类型,有两个功能f :: (A , B) -> Ag :: (A , B) -> B.考虑以下记录类型

data Rec = Rec{_a :: A, _b :: B, _c :: C}.

定义映射(Rec a b c)(Rec (f a b) (g a b) c)使用lens组合器的函数的最优雅方法是什么?

Cir*_*dec 9

镜头

镜头a,b并且c将用手写出fmap(<&>是中缀翻转fmap)

a :: Functor f => (A -> f A) -> Rec -> f Rec
a f (Rec a b c) = f a <&> \a' -> Rec a' b c

b :: Functor f => (B -> f B) -> Rec -> f Rec
b f (Rec a b c) = f b <&> \b' -> Rec a b' c

c :: Functor f => (C -> f C) -> Rec -> f Rec
c f (Rec a b c) = f c <&> \c' -> Rec a b c'
Run Code Online (Sandbox Code Playgroud)

正如cchalmers指出的那样,我们可以扩展这种模式,同时为场_a_b场写镜头

ab :: Functor f => ((A, B) -> f (A, B)) -> Rec -> f Ref
ab f (Rec a b c) = f (a,b) <&> \(a',b') -> Rec a' b' c
Run Code Online (Sandbox Code Playgroud)

结合&&&from Control.Arrow,%~我们可以优雅地编写所需的功能

inAB :: ((A, B) -> A) -> ((A, B) -> B) -> Rec -> Rec
inAB f g = ab %~ (f &&& g)
Run Code Online (Sandbox Code Playgroud)

如果您对镜头库非常熟悉,您可能更愿意使用(ab %~ (f &&& g))而不是inAB f g.

不是一个镜头用于构建的透镜功能ab从透镜ab自两个透镜的产品一般在相同的基础结构不为产品到所述一个底层结构的透镜; 两个镜头都可能试图改变相同的底层场并违反镜头规律.

没有镜头

如果没有镜头,您可以定义一个函数将函数应用于 记录的字段_a_b字段.

onAB :: (A -> B -> c) -> Rec -> c
onAB f r = f (_a r) (_b r)
Run Code Online (Sandbox Code Playgroud)

修饰两者的函数_a_b基于每个功能字段只是设置_a_b对施加到字段中的两个函数的结果.

inAB' :: (A -> B -> A) -> (A -> B -> B) -> Rec -> Rec
inAB' f g r = r {_a = onAB f r, _b = onAB g r}
Run Code Online (Sandbox Code Playgroud)

折腾了几下,curry我们得到了你想要的类型签名

inAB :: ((A, B) -> A) -> ((A, B) -> B) -> Rec -> Rec
inAB f g = inAB' (curry f) (curry g)
Run Code Online (Sandbox Code Playgroud)

镜片较慢,不太优雅

随着镜头,我们也可以说,我们是set荷兰国际集团ab.它不比使用记录构造函数更优雅,它需要构造两次记录.

inAB' :: (A -> B -> A) -> (A -> B -> B) -> Rec -> Rec
inAB' f g r = set b (onAB g r) . set a (onAB f r) $ r
Run Code Online (Sandbox Code Playgroud)

  • 讨论你最后提到的问题的两个问题:http://stackoverflow.com/q/17528119,http://stackoverflow.com/q/23321844.在这个特定的情况下,相关的镜头是合法的,因此原则上从头开始定义它是好的,虽然它可能不值得麻烦. (2认同)
  • 手写它并不是那么糟糕:`_ab f(Rec abc)= f(a,b)<&> \(a',b') - > Rec a'b'c`.那么函数就是`_ab%〜\(a,b) - >(fab,gab)`. (2认同)