镜头水平构图
Horizontal composition of Lenses
箭头有两种构成,垂直:
(.) :: Arrow cat => cat b c -> cat a b -> cat a c
和水平:
(***) :: Arrow cat => cat a b -> cat a' b' -> cat (a,a') (b,b')
(对于任何滥用术语的行为,我向任何范畴理论家道歉)
镜头也可以垂直构图:
(.) :: Lens s t q r -> Lens q r a b -> Lens s t a b
他们能横向排版吗?
使用 van Laarhoven 透镜和混凝土透镜的正常定义:
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
type Lens' s a = Lens s s a a
data ALens s t a b = ALens
{ get :: s -> a
, set :: b -> s -> t
}
type ALens' s a = ALens s s a a
fromALens :: ALens s t a b -> Lens s t a b
toALens :: Lens s t a b -> ALens s t a b
我想出了如何水平组合简单的具体镜头:
along :: ALens' s a -> ALens' s b -> ALens' s (a,b)
along l0 l1 = ALens
{ get = \s -> (get l0 s, get l1 s)
, set = \(a,c) -> set l1 c . set l0 a
}
以及如何使用该定义水平构成简单的 van Laarhoven 镜头:
zip :: Lens' s a -> Lens' s b -> Lens' s (a,b)
zip l0 l1 = fromALens (toALens l0 `along` toALens l1)
在不改变实现的情况下,我什至可以放宽类型签名
along
和 zip
允许其中一个透镜是多态的:
along :: ALens' s a -> ALens s t b c -> ALens s t (a,b) (a,c)
zip :: Lens' s a -> Lens s t b c -> Lens s t (a,b) (a,c)
经过一些手动等式推理,我成功地实现了 zip
转换为具体镜片并返回:
zip l0 l1 h s = h (s ^. l0, s ^. l1) <&> \(a,c) -> s & (l0 .~ a) & (l1 .~ c)
(虽然我当然不相信这是最优雅或最守法的实施)
我还在想是否有一个等价的水平
非简单镜片的成分:
(***) :: Lens r s a b -> Lens s t c d -> Lens r t (a,a') (b,b')
镜头不能有适当的水平构图。当水平组合合法的镜头时,所有的镜头法则都是微不足道的。
只要镜头重叠,您的 zip
就会崩溃。 10 & zip id id .~ (1, 2)
的结果是什么?当镜头目标彼此完全不相交时,您所拥有的将会起作用,所以这没有错。这不是一般的水平构图。输入值还有其他限制。
当您想组合非简单镜头时,情况会变得更糟。现在你也有一个类型问题,即使镜头是不相交的。给定不相交的镜头 lens1 :: Lens s1 t1 a1 b1
和 lens2 :: Lens s2 t2 a2 b2
,它们的水平构图类型是什么? Lens s t (a1, a2) (b1, b2)
当然是一些s
和t
的最终目标。从镜头的工作方式,我们知道 (s ~ s1, s ~ s2)
,所以这不是问题。但是 t
是什么?没有办法计算它。您可能需要按照 "apply the changes made to the type by each" 的方式进行某种操作,但定义不明确。最好的情况是,您可以创建一个类型族来覆盖 s
、t1
和 t2
的特定组合。
箭头有两种构成,垂直:
(.) :: Arrow cat => cat b c -> cat a b -> cat a c
和水平:
(***) :: Arrow cat => cat a b -> cat a' b' -> cat (a,a') (b,b')
(对于任何滥用术语的行为,我向任何范畴理论家道歉)
镜头也可以垂直构图:
(.) :: Lens s t q r -> Lens q r a b -> Lens s t a b
他们能横向排版吗?
使用 van Laarhoven 透镜和混凝土透镜的正常定义:
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
type Lens' s a = Lens s s a a
data ALens s t a b = ALens
{ get :: s -> a
, set :: b -> s -> t
}
type ALens' s a = ALens s s a a
fromALens :: ALens s t a b -> Lens s t a b
toALens :: Lens s t a b -> ALens s t a b
我想出了如何水平组合简单的具体镜头:
along :: ALens' s a -> ALens' s b -> ALens' s (a,b)
along l0 l1 = ALens
{ get = \s -> (get l0 s, get l1 s)
, set = \(a,c) -> set l1 c . set l0 a
}
以及如何使用该定义水平构成简单的 van Laarhoven 镜头:
zip :: Lens' s a -> Lens' s b -> Lens' s (a,b)
zip l0 l1 = fromALens (toALens l0 `along` toALens l1)
在不改变实现的情况下,我什至可以放宽类型签名
along
和 zip
允许其中一个透镜是多态的:
along :: ALens' s a -> ALens s t b c -> ALens s t (a,b) (a,c)
zip :: Lens' s a -> Lens s t b c -> Lens s t (a,b) (a,c)
经过一些手动等式推理,我成功地实现了 zip
转换为具体镜片并返回:
zip l0 l1 h s = h (s ^. l0, s ^. l1) <&> \(a,c) -> s & (l0 .~ a) & (l1 .~ c)
(虽然我当然不相信这是最优雅或最守法的实施)
我还在想是否有一个等价的水平 非简单镜片的成分:
(***) :: Lens r s a b -> Lens s t c d -> Lens r t (a,a') (b,b')
镜头不能有适当的水平构图。当水平组合合法的镜头时,所有的镜头法则都是微不足道的。
只要镜头重叠,您的 zip
就会崩溃。 10 & zip id id .~ (1, 2)
的结果是什么?当镜头目标彼此完全不相交时,您所拥有的将会起作用,所以这没有错。这不是一般的水平构图。输入值还有其他限制。
当您想组合非简单镜头时,情况会变得更糟。现在你也有一个类型问题,即使镜头是不相交的。给定不相交的镜头 lens1 :: Lens s1 t1 a1 b1
和 lens2 :: Lens s2 t2 a2 b2
,它们的水平构图类型是什么? Lens s t (a1, a2) (b1, b2)
当然是一些s
和t
的最终目标。从镜头的工作方式,我们知道 (s ~ s1, s ~ s2)
,所以这不是问题。但是 t
是什么?没有办法计算它。您可能需要按照 "apply the changes made to the type by each" 的方式进行某种操作,但定义不明确。最好的情况是,您可以创建一个类型族来覆盖 s
、t1
和 t2
的特定组合。