Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set value in a nested Map using Lens

I have the following program:

{-# LANGUAGE TemplateHaskell #-}
import qualified Data.Map.Strict as Map
import Control.Lens

data MyLabel = MyLabel { _label :: String } deriving (Show, Eq, Ord)
data MyMap = MyMap { _vals :: Map.Map String MyLabel } deriving (Show, Eq, Ord)
makeLenses ''MyLabel
makeLenses ''MyMap

sample :: MyMap
sample = MyMap { _vals = Map.fromList [("foo", MyLabel "bar")] }

Now I'd like to know how to do a transformation f using lenses such that:

f sample "quux" == MyMap { _vals = Map.fromList [("foo", MyLabel "quux")] }

I learned that the function at from Lens library should be used to modify Maps, so I was trying to do things like this:

sample ^. vals & at "foo" . label .~ Just "quux"

But that produces an error message which is not very understandable for me. What is the right way to do this?

like image 824
user1747134 Avatar asked Feb 01 '26 02:02

user1747134


1 Answers

Try this on for size:

{-# LANGUAGE TemplateHaskell #-}

module Main where

import qualified Data.Map.Strict as Map
import Control.Lens

data MyLabel =
  MyLabel { _label :: String } deriving (Show, Eq, Ord)

data MyMap =
  MyMap { _vals :: Map.Map String MyLabel } deriving (Show, Eq, Ord)

makeLenses ''MyLabel
makeLenses ''MyMap

sample :: MyMap
sample =
  MyMap (Map.fromList [("foo", MyLabel "bar")])

main :: IO ()
main =
  print (sample & (vals . at "foo" . _Just . label .~ "quux"))

Remember that, when setting, you're trying to build a function of type MyMap -> MyMap. The way you do that is by chaining a bunch of optics together (vals . at "foo" . _Just . label) and then choosing a setter operation (.~). You can't mix and match a getter operation like ^. with a setter operation like .~! So every setter more or less looks like this:

foo' = (optic1 . optic2 . optic3 . optic4) .~ value $ foo
--     _________this has type Foo -> Foo___________

And to improve readability we use &, the flipped version of $:

foo' = foo & (optic1 . optic2 . optic3 . optic4) .~ value
like image 184
hao Avatar answered Feb 02 '26 14:02

hao