这是什么类型的镜头组合?

时间:2014-04-04 14:10:43

标签: ruby haskell lenses

我正在用Ruby制作一个镜头组合器,我无法弄清楚它的普遍接受的名称是什么。未知函数组成两个具有相同源类型的镜头,它们的目标类型(使用Benjamin C. Pierce’s terminology)是一个哈希映射。未知功能可以接收这两个镜头并返回一个与原始镜头具有相同光源类型和目标类型的新镜头。

看起来像这样(Ruby语法):

lens_a.get(source)
> {:title => "some title"}
lens_b.get(source)
> {:description => "some description"}
new_lens = unknown_function(lens_a, lens_b)
new_lens.get(source)
> {:title => "some title", :description => "some description"}

我正在尝试构建的组合子的图表可以在this presentation的幻灯片18上看到(幻灯片的标题是“合并?”)。

我看过Haskell的lens docs(其中我能理解的一小部分),但我无法弄清楚这是哪个组合。

上面的unknown_function的标准名称是什么?如果这个镜头没有标准名称,是否有一些标准功能可以组成它?如果没有,我可能只是称它为合并。

2 个答案:

答案 0 :(得分:5)

我相信在Haskell中,你的组合器大致正确的想法是

merge :: Lens' s t -> Lens' s t -> Lens' s (t, t)

可能推广到

merge :: Lens' s t -> Lens' s t' -> Lens' s (t, t')

这样您的两个目标的类型可能会有所不同。我们可以实施"如下所示,但这样做会显示问题

merge l1 l2 = lens pull push where
  pull s          = (view l1 s, view l2 s)
  push s (t1, t2) = set l2 t2 (set l1 t1 s)

特别是,在此等式的Setter方面,我们明确命令我们放回价值的方式。这意味着我们可以使用merge轻松制作违反镜头规律的镜头。特别是,它们违反了PUT-GET法。这是一个反例

newtype Only a = Only a

-- A lens focused on the only slot in Only
only :: Lens' (Only a) a
only inj (Only a) = Only <$> inj a

-- We merge this lens with *itself*
testLens :: Lens' (Only a) (a, a)
testLens = merge only only

-- and now it violates the lens laws
> view testLens (Only 1 & testLens .~ (1,2))
(2,2)

或者,用简单的英语,如果我们merge镜头本身,那么Setter侧会在同一位置变成两个连续的集合。这意味着如果我们尝试使用一对不同的值设置它,那么 second 集将会持续存在,因此我们违反了PUT-GET法则。

lens库往往会避开无效镜头,因此这个组合器不可用。我们最接近的是alongside,它具有以下(限制)类型

alongside :: Lens'  s     a
          -> Lens'  s'    a'
          -> Lens' (s,s') (a,a')

但是,正如您所看到的,这可确保我们的也是一种产品类型,以便Setter唯一地应用于源的每一侧。如果我们尝试编写dup Lens我们可以使用alongside撰写merge以构建dup :: Lens' a (a, a) -- there are two possible implementations, neither are lenses dup1 inj a = fst <$> inj a dup2 inj a = snd <$> inj a ,我们将遇到与之前相同的问题

merge

现在,从Ruby的角度来看,所有这些都可能具有相当的技术性和无意义。在Ruby中,您不会让编译器强制执行类型安全,因此您可能会将许多规则混合在一起并实现较低的严格性。在这种情况下,使用其顺序写入语义实现merge可能是有意义的。

事实上,基于你给出的例子,看起来我指出的反例甚至不会发生在Ruby中,因为你的目标类型是Hash并且已经确保了唯一键。

但重要的是要注意merge可以用来构建不正确的镜头,如果你足够努力的话。从根本上说,这意味着在更复杂的情况下,使用{{1}}很容易导致由于它违反我们对镜头的直观感觉而导致的奇怪错误。这对于Ruby来说可能不是一个大问题,因为复杂的抽象在Ruby社区中是不受欢迎的,但这就是我们制定法律的原因。

答案 1 :(得分:0)

似乎想要将两个镜头组合成traversal。我没有在任何地方看到组合器,但我是镜头的新手,所以我不确定它会被称为是什么。

类似的东西:

compoundEye :: Traversable tl => tl (Lens s t a b) -> Traversal s t a b

但是,the hoogle results并不乐观。