使用`at`和`ix`编写镜头

Chr*_*lor 6 haskell traversal haskell-lens

假设我有一些相当简单的数据类型Person,包含几个字段,以及一个包含Persons 集合的类型.

data Person = Person { _name :: String, _age  :: Int }

data ProgramState = PS { _dict :: IntMap Person }

makeLenses ''Person
makeLenses ''ProgramState
Run Code Online (Sandbox Code Playgroud)

我想创建一个镜头,让我通过查找他们的密钥来访问个人

person :: Int -> Lens' ProgramState Person
Run Code Online (Sandbox Code Playgroud)

看来我这样做的两个选择是使用atix索引到字典中

-- Option 1, using 'at'
person :: Int -> Lens' ProgramState (Maybe Person)
person key = dict . at key

-- Option 2, using 'ix'
person :: Int -> Traversal' ProgramState Person
person key = dict . ix key
Run Code Online (Sandbox Code Playgroud)

但是这些选项都没有让我做我想做的事,Lens'也就是说有一个访问Person而不是a Maybe Person.选项1与其他镜头不能很好地搭配,选项2意味着我必须放弃我的吸气剂.

我理解为什么 ix并且at写得像这样.密钥可能不存在于dict中,因此如果你想要一个Lens'同时启用getter和setter 的密钥,它必须访问一个Maybe a.另一种方法是接受一个Traversal'允许访问0或1值的a,但这意味着放弃你的getter.但在我的情况下,我知道我想要的元素将永远存在,所以我不需要担心丢失键.

有没有办法写出我想写的东西 - 或者我应该重新考虑我的程序结构?

And*_*ács 5

您可能希望atnon同构一起使用.您可以使用它指定默认映射条目以消除Maybe查找.

non :: Eq a => a -> Iso' (Maybe a) a

person key = dict . at key . non defaultEntry

-- can get and set just like plain lenses
someProgramState & dict . at someKey . non defaultEntry .~ somePerson
Run Code Online (Sandbox Code Playgroud)

您可以在文档中查看更多示例.