为自定义镜头编写类别实例

时间:2014-03-14 12:26:21

标签: haskell lens

我一直在阅读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

在此之后,我只是整天凝视它。到底是什么 写下它的定义的思考过程?

3 个答案:

答案 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 -> ygetA :: 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 .引用idf . 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)