我发现这篇文章(Joseph Abrahamson 在 fpcomplete 上从零开始的镜头)非常好,它从与您开始时相同的镜头表示开始,为它定义了构图,并沿着路径继续到更类似于镜头的表示
编辑:在做这种事情时,我发现类型孔非常好:
(<.>):: 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
仔细查看绑定,我们已经拥有了我们需要的东西!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
which type 检查,但没有给我们想要的东西(设置时没有任何反应)。唯一可以给我们的其他绑定x
是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