我一直在阅读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
在此之后,我只是整天凝视它。到底是什么 写下它的定义的思考过程?
答案 0 :(得分:4)
我发现这个article(从由约瑟夫阿布拉汉森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
仔细观察绑定,我们已经拥有了我们需要的东西! 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
的唯一其他绑定是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
答案 1 :(得分:1)
试试这个:
(.) :: Lens y z -> Lens x y -> Lens x z
(getZfromY , setZinY) . (getYfromX , setYinX) = (getZfromX , setZinX)
where getZfromX someX = ...
setZinX someZ someX = ...
这个想法是:结合两个吸气剂来制造新的吸气剂,然后将两个吸气剂结合起来制作一个新的吸气剂。
对于身份,请考虑:
id :: Lens x x
id = (getXfromX , setXinX)
where getXfromX someX = ...
setXinX newX oldX = ...
答案 2 :(得分:1)
这似乎是一个相当直接的过程。但是还需要检查你是否得到了一个类别 - 这需要等式推理 - 因为,例如,至少有一种方法可以实现类型为id
的{{1}}的setter - 只有其中一个将成为一个类别。
所以,让我们从获得正确类型的函数开始。
x->x->x
似乎很清楚如何从Lens y z -> Lens x y -> Lens x z ==
(y->z, z->y->y) -> (x->y, y->x->x) -> (x->z, z->x->x)
和x->z
获得x->y
- 撰写。好吧,你有办法从旧的y->z
和新的x
构建新的x
,以及从旧的y
中获取旧y
的方法,所以如果你可以从x
和旧y
构建新的z
,你就完成了。
y
同样适用于id:
(.) (yz, zyy) (xy, yxx) = (yz . xy, \z x -> yxx (zyy z (xy x)) x)
所以
Lens x x ==
(x->x, x->x->x)
到目前为止这么好,类型检查。现在让我们检查一下我们是否有类别。有一条法律:
id = (id, const)
检查一种方式(有点非正式,因此需要注意f . id = f = id . f
和.
引用id
和f . id
中的不同内容:
fg . id
检查另一种方式:
f . id = (fg, fs) . (id, const) =
(fg . id, \z x -> const (fs z (id x)) x) =
(fg, \z x -> fs z (id x)) = (fg, fs)