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

bma*_*ddy 1 ruby haskell lenses

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

它看起来像这样(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"}
Run Code Online (Sandbox Code Playgroud)

我正在尝试构建的组合器的图表可以在本演示文稿的幻灯片18上看到(幻灯片的标题是"合并?").

我看过Haskell的镜头文档(我可以理解其中的一小部分),但我无法弄清楚这是哪个组合器.

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

J. *_*son 5

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

merge :: Lens' s t -> Lens' s t -> Lens' s (t, t)
Run Code Online (Sandbox Code Playgroud)

可能推广到

merge :: Lens' s t -> Lens' s t' -> Lens' s (t, t')
Run Code Online (Sandbox Code Playgroud)

这样你的两个目标的类型可能不同.我们可以"实现"如下,但这样做会显示出一个问题

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)
Run Code Online (Sandbox Code Playgroud)

特别是,在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)
Run Code Online (Sandbox Code Playgroud)

或者,用简单的英语,如果我们merge自己的镜头,那么Setter侧面在同一位置变成两个连续的集合.这意味着如果我们尝试用一对不同的值设置它,那么只有第二组会持续存在,因此我们违反了PUT-GET定律.

lens库往往避开无效的镜头所以这个组合子不可用.我们最接近的是alongside具有以下(限制)类型

alongside :: Lens'  s     a
          -> Lens'  s'    a'
          -> Lens' (s,s') (a,a')
Run Code Online (Sandbox Code Playgroud)

但是,正如您所看到的,这可以确保我们的也是一种产品类型,以便Setters唯一地应用于源的每一侧.如果我们要尝试编写一个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
Run Code Online (Sandbox Code Playgroud)

现在,从Ruby的角度来看,所有这些都可能具有相当的技术性和无意义.在Ruby中,你不会有编译器强制类型安全性,所以你可能会将很多规则混合在一起并且实现不那么严格.在这种情况下,merge使用其顺序写入语义实现可能是有意义的.

实际上,根据您给出的示例,看起来我指出的反例甚至不会发生在Ruby中,因为您的目标类型是Hash并且已经确保了唯一键.

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