使用 Lenses 使用来自 IO 的数据填充某些复杂结构的正确方法是什么?

What is the proper way to fill some complex structure with data from IO using Lenses?

{- Some code to illustrate what I mean: -}
module Main where

import System.IO
import Control.Monad
import Control.Lens
import Data.UUID
import Data.UUID.V4

-- I have complex data structure (initially, JSON) and I can use
-- Data.Aeson.Lens to work with this structure. It's pretty comfortble
-- in most cases. But let's suppose we have broken data structure.
-- Instead of "a" I need to have unique key. Easiest way is to use GUID.
ds = [ [("a", "z"),("a", "x"),("a", "c")]
     , [("a", "v"),("a", "b"),("a", "n")]
     , [("a", "m"),("a", ","),("a", ".")]
     ]

-- So that's how we can get unique ID.
getId :: IO String
getId = toString <$> nextRandom

main :: IO ()
main = do
-- To insert unique ID I can use a awkward way like this:
-- generate pool of ids that is big enough.
    let count = length $ ds ^.. traversed <.> traversed
    ids <- forM [0..count * count] $ const getId
-- use Indexed Lens to have list of IDs for each data structure level
-- and insert ID from pre generated list where I need it
    let ds' = ds & traversed <.> traversed %@~
                 (\(i1, i2) v -> v & _1 %~
                    (\v' -> (ids !! (i1 * count + i2)) ++ v'))
    print ds'
-- It works. But I wonder if there is some better way to do the same.

是的,有更好的方法。您只是错过了一些组合镜头优惠。我建议 traverseOf,尽管它在镜头库中还有其他几个别名...而且所有这些别名都只是实现为 id...

ds' <- traverseOf (traverse . traverse . _1) (const getId) ds