3

我一直在阅读这篇文章以了解 Lenses。我知道这与 Edward Knett 的镜头包不同,但它对基础知识很有用。

因此,A Lens 的定义如下:

type Lens a b = (a -> b, b -> a -> a)

有人提到镜头形成一个类别,我一直在尝试为Categorytypeclass 创建一个实例。首先,我为函数编写了类型定义:

(.) :: Lens y z -> Lens x y -> Lens x z
id :: Lens x x

在此之后,我只是盯着它一整天。编写它的定义的思考过程到底是什么?

4

3 回答 3

4

我发现这篇文章(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 -> 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

我们可以插入xwhich type 检查,但没有给我们想要的东西(设置时没有任何反应)。唯一可以给我们的其他绑定xsetB,所以我们插入:

(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可以给我们一个,我们碰巧有,但这会导致我们再次无用的镜头无所事事。另一种方法是使用:yxsetA

(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

于 2014-03-14T12:37:30.463 回答
1

尝试这个:

(.) :: Lens y z -> Lens x y -> Lens x z
(getZfromY , setZinY) . (getYfromX , setYinX) = (getZfromX , setZinX)
             where getZfromX someX = ...
                   setZinX someZ someX = ...

这个想法是:将两个 getter 组合成新的 getter,将两个 setter 组合成一个新的 setter。

对于身份,请考虑:

id :: Lens x x
id = (getXfromX , setXinX)
    where getXfromX someX = ...
          setXinX newX oldX = ...
于 2014-03-14T12:42:44.620 回答
1

这似乎是一个相当简单的过程。但还需要检查您是否获得了一个类别——这需要等式推理——因为,例如,至少还有一种方法可以实现idwith type的 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->zx->yy->z- 组合。好吧,你有办法x从旧x和新构造新,以及从旧构造y变老的方法,所以如果你能从旧构造新,你就完成了。yxyzy

(.) (yz, zyy) (xy, yxx) = (yz . xy, \z x -> yxx (zyy z (xy x)) x)

同样对于 id:

Lens x x ==
(x->x, x->x->x)

所以

id = (id, const)

到目前为止一切顺利,类型检查。现在让我们检查一下我们是否有一个类别。有一条法律:

f . id = f = id . f

检查一种方式(有点非正式,所以需要记住这一点.并在andid中引用不同的东西):f . idfg . 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)

检查另一种方式:

id . f = (id, const) . (fg, fs) =
(id . fg, \z x -> fs (const z (fg x)) x) =
(fg, \z x -> fs z x) = (fg, fs)
于 2014-03-14T14:42:12.120 回答