我一直在阅读这个 article 以了解镜头。我知道这与
Edward Knett 的镜头包,但它对基础知识很有用。
因此,A Lens 定义如下:
type Lens a b = (a -> b, b -> a -> a)
有人提到镜头形成一个类别,我一直在
尝试为
Category
类型类创建一个实例。首先,我编写函数的类型定义:
(.) :: Lens y z -> Lens x y -> Lens x z
id :: Lens x x
在这之后,我只是盯着它一整天。究竟是什么
编写定义的思考过程?
最佳答案
我发现这个 article(Joseph Abrahamson 在 fpcomplete 上从零开始的镜头)非常好,它从你开始使用的相同镜头表示开始,定义了它的构图,并沿着路径继续前进到更类似于 lens 的表示
编辑:在做这种事情时,我发现类型孔非常好:
(<.>):: Lens y z -> Lens x y -> Lens x z
(getA,setA) <.> (getB,setB) = (_,_)
所以现在我们有 2 个孔,元组中的第一个说(输出已清理):
Found hole ‘_’ with type: x -> z
...
Relevant bindings include
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
(<.>) :: Lens y z -> Lens x y -> Lens x z
仔细查看绑定(bind),我们已经拥有了我们需要的东西!
getB :: x -> y
和 getA :: y -> z
与函数组合 (.) :: (b -> c) -> (a -> b) -> a -> c
所以我们很高兴地插入这个:
(<.>):: Lens y z -> Lens x y -> Lens x z
(getA,setA) <.> (getB,setB) = (getA . getB, _)
并继续第二种类型的孔,它说:
Found hole ‘_’ with type: z -> x -> x
Relevant bindings include
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
我们拥有的最相似的是
setA :: z -> y -> y
,我们首先插入一个 lambda,捕获参数:(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> _)
将您的类型孔更改为:
Found hole ‘_’ with type: x
Relevant bindings include
x :: x
z :: z
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
我们可以插入类型检查的
x
,但不会给我们想要的东西(设置时什么也没发生)。唯一可以为我们提供 x
的其他绑定(bind)是 setB
,因此我们插入:(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB _ _)
我们的第一种类型孔说:
Found hole ‘_’ with type: y
Relevant bindings include
x :: x
z :: z
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
所以我们需要一个 y,看看范围内的内容,如果我们给它一个
getB
, y
可以给我们一个 x
,我们碰巧有,但这会导致我们再次无用的镜头什么都不做。另一种方法是使用 setA
:(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA _ _) _)
(从这里开始加快速度)
同样,第一个洞需要
z
类型的东西,他碰巧将它作为我们 lambda 的参数:(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA z _) _)
为了填充
y
类型的第一个类型孔,我们可以使用 getB :: x -> y
给它我们的 lambda 参数:(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA z (getB x)) _)
这给我们留下了一个剩余的类型漏洞,可以简单地替换为
x
,导致最终定义:(<.>):: Lens y z -> Lens x y -> Lens x z
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA z (getB x)) x)
您可以尝试为自己定义
id
,必要时使用类型孔和 hoogle关于haskell - 自定义镜头的书写类别实例,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22404939/