user1747134
user1747134

Reputation: 2472

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?

Upvotes: 3

Views: 1068

Answers (2)

user1747134
user1747134

Reputation: 2472

The problem was actually caused by cabal hell and cleaning up ~/.ghc directory was necessary to make it work.

Upvotes: -1

hao
hao

Reputation: 10228

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

Upvotes: 9

Related Questions