镜头法:他们试图告诉我什么?

时间:2017-02-15 01:33:12

标签: lenses

我已经看过各种版本的镜头法。不确定他们是否都是等同的,所以为了明确我会在StackOverflow上使用标签Lenses [EdwardKmett~5年前]

(我问,因为我想要更多地处理双向编程。)

使用a作为结构,b作为结构中的组件/值:

  1. get (set b a) = b
  2. 确定。你得到的就是你所做的。对于自称为数据结构/容器的任何事物来说似乎都是必不可我可能会有轻微的问题:初始a来自哪里?我可以直接去get a吗?那是什么意思?

    1. get (set b' (set b a)) = b'
    2. ?我相信这是为了告诉我:你得到的是你上次放的东西(以及你之前放的东西永远丢失)。但实际上并没有这么说。它没有(例如)排除镜头是一个堆栈 - a - 即get表现得像pop。因此,如果我再做一次get,它可能会返回早期的b。如果它需要说:一旦你set b' (whatever-a)get总是会无限地返回b'

      这是法律有时以以下形式编写:set b' (set b a) = set b' a。但我根本不喜欢这样,这让我想到:

      1. set (get a) a = a
      2. 把你已经拥有的东西都没有用。 (这似乎是一个很难说的事情:它不是从法律1开始的吗?)但是对结构的一个相等测试正在打破抽象。我们(作为结构的客户)并不关心结构如何在内部组织起来。我们的界面是方法getset。把你已经拥有的东西放在一起可能会改变我们所关心的所有结构的价值 - 只要get返回我们所放的那个值。

        如果set (get a) a的价值/内容至关重要,那么可以用get / set来表达吗?如果它不能,我们为什么要关心?

        所有这些法律都是单镜头的。因此,如果结构只是一个单一的插槽,他们就会坚持下去。 - 对于一个名为“变量”的东西而言似乎有很多机器。

        似乎缺少的是关于如何通过更复杂的结构组合不同镜片的任何事情。这样的结构允许每个透镜正交工作。我相信那里有一个van Laarhoven法律:

        -- I need two lenses, so I'll use get', set' as well as get, set get' (set b (set' b' a)) = b'

        我不需要这样的法律吗?请解释一下。

2 个答案:

答案 0 :(得分:0)

我之前没有使用过这种形式,所以它可能会在我的回答中显示出来。

  1. 这是一部数学定律。 a来自哪里无关紧要,只要它满足相关的数学要求即可。在编程中,您可以在代码中定义它,从文件加载它,从网络调用解析它,递归地构建它;不管。

  2. 不能这么说。 get不返回新结构,只返回值;镜头的重点是为不可变,无副作用的结构创建一个框架。让a'(set b' (set b a)); get a'每次都会返回b',因为a'无法更改,get是纯函数;国家没有存放的地方。对于纯函数,您的“get将永远返回b' 无限广告”被认为始终为真,不需要进一步的规定。要实现堆栈,您需要以下两种方法之一:要么是可变的,要么是有效的(因此get a == get a不一定是真的),或者操作函数需要返回新的堆栈 - {{{ 1}}和put

  3. 我没有为此构建一个反例,可能是因为它直观地如此强烈。这是一个非常微弱的反例:让getget a (Container b _) = bset b (Container b c) = Container b (c + 1)。此外,请set b (Container b' _) = Container b 0a = Container b' 0。第一定律:b != b' - 好的。第二定律:get (set b a) = get (Container b 0) = b - 好的。但是,get (set b' (set b a)) = get (set b' (Container b 0)) = get (Container b' 0) = b' - 不行。因此,它不遵循第一定律。如果没有这个,你就无法测试两个结构是否相等,而是需要迭代每个访问器来证明两个结构是相等的(作为一个JavaScript程序员,让我告诉你:没有对象标识功能真的很尴尬)。

  4. 你这样做。想象一下:set (get a) a = set (get (Container b' 0)) (Container b' 0) = set b' (Container b') = Container b' 1 != aset b a = Container b。第一定律:get (Container b) = b - 好的。第二定律:get (set b a) = get (Container b) = b - 好的;第三定律:让get (set b' (set b a)) = get (set b' (Container b)) = get (Container b') = b'a == Container b - 好的。所有三个定律都满足于这个非常简单(和明显错误的)定义。现在让我们添加set (get a) a = set (get (Container b)) a = set b a = Container b = aset' b a = Container' b),看看会发生什么:get' (Container' b) = b ...这无法得到评估。 Oopsie。或者,想象一下:get' (set b (set' b' a)) = get' (set b (Container' b')) = get' (Container b)set b a = Container b 0get (Container b _) = bset' b a = Container b 1。在这种情况下,get' (Container b _) = b - 不行。该法律保证get' (set b (set' b' a)) = get' (set b (Container b' 1)) = get' (Container b 0) = b设置的值将保留在结构中,即使我们应用set'(在此示例中肯定不是这种情况)。

答案 1 :(得分:0)

如果你对双向编程有所期待,我很惊讶你很困惑,AntC。

FAQ页面资源[1]引用了Pierce和Voigtländer(两者都有相似的法律规定);但实际上他们正在使用完全不同的语义。例如Pierce等人的2005年论文:

  

镜头的 get 组件完全对应于视图定义。为了支持组合方法,我们认为视图状态是一个完整的数据库(而不仅仅是一个单一的关系,就像许多视图处理一样)。

因此,这些是数据库意义上的“观点”(这导致了对光学术语的惩罚)。镜头始终存在“基础”模式定义(数据结构),镜头可以从中定义视图。 Pierce和Voigtländer都试图管理数据结构的“大小和形状”,以保持它们之间的转换。难怪他们认为“基础”是内容的主要持有者,而镜头只是透视的机制。

通过功能参考,没有这样的困难。镜头专注于单个数据结构中的“插槽”。结构被视为抽象。

如果功能参考方法想要根据'插槽'制定法则,并且专门处理皮尔斯称之为不经意的镜头,那么它必须采用外部结构部分的语义镜头。 Voigtländer等人2012年的论文涉及使用 res (残留)函数;或之前关于数据库视图的工作[Bancilhon& Spyratos 1981]称之为“补语”。

O.P.中引用的法律中的set函数是无视,因为它忽略了'slot'的当前内容并覆盖它。镜头不一定是那样的。最近的处理使用upd函数(使用附加参数f来更新以应用于当前值)。注意

get (upd f a) = f (get a).
get (set b a) = b = get (upd (const b) a).   (Law 1)

除了并非所有镜头upd操作都遵守这些等效性。例如,getday时段的镜头的巧妙技巧;但是upd (+ 1)增加了整个日期。 getDay (updDay (+ 1) (Date {year = 2017, month = 2, day = 28}) )返回1,而不是29

关于O.P。

中的具体问题
  1. “轻微q”:初始a来自create函数[Voigtländer] 并且有很多create / get法律。
    • 或已经在'基础'架构[Pierce]。
  2. @Amadan的回答是正确的钱。 getset是纯函数。
      根据法律规定,
    • get必须返回最后set的内容。因此,法律2似乎毫无意义。
  3. 对Pierce和Voigtländer来说很重要,因为他们认为“基地”是至关重要的。
    • 对于功能引用(如果数据结构应该是抽象的),
    • 说明这项法律正在打破抽象。
    • 它也没有说明其他镜头进入结构的行为, 如果set 更改了值 - 这肯定是镜头用户想要了解的内容。
    • 所以再次看起来毫无意义。
    • 请注意,Pierce和Voigtländer的方法都不会超过一个镜头。
  4. 如果两个镜头专注于结构中的独立槽,则这两个镜头都保持:

    -- continuing to use get'/set' as well as get/set
     ∀ b b' . get' (set b (set' b' a)) = b'     -- law for set' inside set
     ∀ b' b . get (set' b' (set b a)) = b       -- law for set inside set'
    

    如果两个镜头干扰/重叠,则两个方程都不成立(通常用于setset'域中的所有值。

    所以,从getDay / updDay内部getDate / setDate开始,采取“最差”但仍然合理的案例:getDate / setDate 法律1适用于updDay / set;但Firebase ref; Firebase.setAndroidContext(getActivity()); ref = new Firebase(YOUR_FIREBASE_URL); 的行为与ref.child("universities/-KNk0.....") .addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { try { if (dataSnapshot.getValue() != null) { Map<String, Object> value = (Map<String, Object>) dataSnapshot.getValue(); namaprofil.setText(value.get("name") ); } } catch (Exception e) { e.printStackTrace(); } } @Override public void onCancelled(FirebaseError firebaseError) { } }); 的版本不同。 法律2&amp; 3持有(但似乎毫无意义)。

    我们没有任何法律可以有效地写出他们的互动。 我想我们能做的最好的事情是将镜头聚焦在同一个结构中  进入/不相互干涉的分组。

    总的来说,我不认为镜片法在理解镜片方面有很大帮助,因为它们现在已经被使用了。

    [1] https://github.com/ekmett/lens/wiki/FAQ#lens-resources