用于嵌套容器的索引透镜

Indexed lenses for nested containers

如何使用lens获取多级嵌套的key?

考虑以下类型

data Outer = Outer { _outerMap :: Map String Inner }
data Inner = Inner { _innerMap :: Map Char Int }
makeLenses ''Outer
makeLenses ''Inner

并假定以下示例值

example :: Outer
example = Outer $ Map.fromList
  [ ("A", Inner $ Map.fromList [ ('a', 1), ('b', 2), ('c', 3) ])
  , ("B", Inner $ Map.fromList [ ('a', 4), ('b', 6), ('c', 8) ])
  , ("C", Inner $ Map.fromList [ ('a', 5), ('b', 7), ('c', 9) ])
  ]

使用镜头我可以将 example 展平为 [Int] 并按如下方式过滤奇数:

>>> example^..outerMap.folded.innerMap.folded.filtered odd
[1,3,5,7,9]

我可以用内键注释值如下:

>>> example^@..outerMap.folded.innerMap.ifolded.filtered odd
[('a',1),('c',3),('a',5),('b',7),('c',9)]

但是我怎样才能使用镜头来注释外键和内键的值,以获得以下结果?

>>> _whatHere example
[(("A",'a'),1),(("A",'c'),3),(("C",'a'),5),(("C",'b'),7),(("C",'c'),9)]

下面的尝试仍然只有returns内键:

>>> example^@..outerMap.ifolded.innerMap.ifolded.filtered odd
[('a',1),('c',3),('a',5),('b',7),('c',9)]

并且以下尝试不进行类型检查

>>> example^..outerMap.ifolded.withIndex.alongside id (innerMap.ifolded.filtered odd.withIndex)

error:
    • No instance for (Applicative
                         (Control.Lens.Internal.Getter.AlongsideRight
                            (Const (Data.Monoid.Endo [([Char], (Char, Int))])) [Char]))

没有镜头的实现可能看起来像这样:

nolens :: Outer -> [((String, Char), Int)]
nolens =
  filter (odd . snd)
  . foldMap (\(k, i) -> (map (first (k, )) . Map.toList . _innerMap) i)
  . Map.toList
  . _outerMap

使用(<.>)。它就像 (.),只是它保留了左右两边的索引。 (.) 本身(及其别名 (.>))仅保留 RHS 的索引,除非 RHS 本身是保留索引的,在这种情况下,索引来自 LHS。助记符是箭头指向您要保存的索引。

>>> example^@..outerMap.ifolded<.>innerMap.ifolded.filtered odd
[(("A",'a'),1),(("A",'c'),3),(("C",'a'),5),(("C",'b'),7),(("C",'c'),9)]