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

时间:2015-09-16 19:47:17

标签: haskell types 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的函数的最优雅方法是什么?

1 个答案:

答案 0 :(得分:9)

镜头

镜头abc将以fmap<&>为中缀翻转fmap)手写为< / p>

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'

正如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

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

inAB :: ((A, B) -> A) -> ((A, B) -> B) -> Rec -> Rec
inAB f g = ab %~ (f &&& g)

如果您对镜头库感到满意,可能更倾向于使用(ab %~ (f &&& g))代替inAB f g

isn't lens功能,用于从镜头aba构建镜头b,因为一般来说,两个镜头的产品在同一镜头上底层结构不是产品在一个底层结构上的透镜;两个镜头都可能试图改变相同的底层场并违反镜头规律。

没有镜片

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

onAB :: (A -> B -> c) -> Rec -> c
onAB f r = f (_a r) (_b r)

根据每个函数修改_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}

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

inAB :: ((A, B) -> A) -> ((A, B) -> B) -> Rec -> Rec
inAB f g = inAB' (curry f) (curry g)

较慢,不太优雅的镜片

使用镜头我们也可以说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