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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With