我所知道的是一个有效,另一个没有。
上下文
我有一个数据结构F
,其中包含Data.Map.Map k S
到另一个数据结构S
。我的目标是构建一个Lens
,其中F
和k
会在S
中描述一个字段。
难点在于地图中可能不存在键k
。这很好,该函数可以在Maybe中包装它的返回。但是我无法通过使用at
的Maybe传播镜头。在阅读了大量Stack Overflow答案后,我遇到了this one。
事实证明,将at
替换为ix
解决了我的类型问题如果我还将(^.)
替换为(^?)
。
问题:
似乎at
和ix
做同样的事情,至少在Map
方面。两者都拿一把钥匙并给那个钥匙的价值一个'镜头'。但是,ix
似乎与函数组合运算符(.)
配合得很好。这两者有什么区别?
Off Topic Rant:
我和下一个人一样喜欢中缀运算符但是Control.Lens包看起来有点过分了。对于具有某些英文名称和某个键的新用户而言,会降低学习曲线。由于Lens库中使用了大量的包装器类,如果您还不知道发生了什么,则特别难以深入了解类型签名。为了天堂,我的代码开始看起来像Perl。
答案 0 :(得分:22)
如果查看包含这些函数的类的可用实例,at
和ix
不同已经很明显了:
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 b
,a ?~ b
)
从技术上讲,这种差异来自ix
是Traversal而at
是Lens的事实。而且因为at
是一个“返回”一个可能的东西的镜头,你不能用一个只带一个简单的“Something”的镜头来构图。 ix
是一个具有0或1值的遍历,因此您可以像任何其他遍历一样编写ix(就像您可以编写traverse . traverse
一样)。 (^?)
只占用该遍历的第一个值(head)。
您始终可以从ix
at
ixAt = at . traverse
相同的定义is already in lens,除了它使用(<.)
用于保持索引的组合。 (at
和ix
都是索引镜头/遍历)。
偏离主题:镜头中的大多数操作员也有中缀名称,您可以在https://github.com/ekmett/lens/wiki/Operators
找到(不完整)表格