在Haskell的Lens库中,`ix`和`at`有什么区别?

时间:2013-08-24 01:58:15

标签: haskell lens

我所知道的是一个有效,另一个没有。

上下文 我有一个数据结构F,其中包含Data.Map.Map k S到另一个数据结构S。我的目标是构建一个Lens,其中Fk会在S中描述一个字段。

难点在于地图中可能不存在键k。这很好,该函数可以在Maybe中包装它的返回。但是我无法通过使用at的Maybe传播镜头。在阅读了大量Stack Overflow答案后,我遇到了this one

事实证明,将at替换为ix解决了我的类型问题如果我还将(^.)替换为(^?)

问题: 似乎atix做同样的事情,至少在Map方面。两者都拿一把钥匙并给那个钥匙的价值一个'镜头'。但是,ix似乎与函数组合运算符(.)配合得很好。这两者有什么区别?


Off Topic Rant:

我和下一个人一样喜欢中缀运算符但是Control.Lens包看起来有点过分了。对于具有某些英文名称和某个键的新用户而言,会降低学习曲线。由于Lens库中使用了大量的包装器类,如果您还不知道发生了什么,则特别难以深入了解类型签名。为了天堂,我的代码开始看起来像Perl。

1 个答案:

答案 0 :(得分:22)

如果查看包含这些函数的类的可用实例,atix不同已经很明显了:

  • At的实例:Map,IntMap,HashMap
  • Ixed的实例:[a],Map,ByteString,Text等等

At的所有实例也是Ix的实例,但Ix的所有实例都不是At的实例。

那么它们之间的区别是什么? At用于允许插入容器中不存在的键的容器。这显然可以用于地图,但不是例如列表。为了仍然能够索引到列表并更改那里的项目,Ix不允许创建新项目,但是当您尝试写入不存在的项时,只会“无所事事”。

>>> Data.Map.fromList [('a', 1)] & at 'b' .~ Just 4 
fromList [('a',1),('b',4)] -- Inserts value 
>>> Data.Map.fromList [('a', 1)] & ix 'b' .~ 4  
fromList [('a',1)]          -- Does nothing because key is not present

(还有a .~ Just ba ?~ b

的快捷方式

从技术上讲,这种差异来自ixTraversalatLens的事实。而且因为at是一个“返回”一个可能的东西的镜头,你不能用一个只带一个简单的“Something”的镜头来构图。 ix是一个具有0或1值的遍历,因此您可以像任何其他遍历一样编写ix(就像您可以编写traverse . traverse一样)。 (^?)只占用该遍历的第一个值(head)。

您始终可以从ix

派生at
ixAt = at . traverse

相同的定义is already in lens,除了它使用(<.)用于保持索引的组合。 (atix都是索引镜头/遍历)。

偏离主题:镜头中的大多数操作员也有中缀名称,您可以在https://github.com/ekmett/lens/wiki/Operators

找到(不完整)表格