How do you compose _Just with the at lens

Viewed 96

I'm not able to figure out how to compose nicely access to a Data.Map with a lens accessing a record. Say that I have:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens
import Control.Lens.TH
import Data.Map

data Person =
  Person
    { _age :: Int
    , _name :: String
    }
  deriving (Show)

makeLenses ''Person

database = fromList [(1, Person 35 "John")]

What would be the one liner that allows getting John's age? Because

johsnAge = database ^. at 1 . _Just . age

Is giving me error:

    • No instance for (Monoid Int) arising from a use of ‘_Just’
    • In the first argument of ‘(.)’, namely ‘_Just’
      In the second argument of ‘(.)’, namely ‘_Just . age’
      In the second argument of ‘(^.)’, namely ‘at 1 . _Just . age’
   |
18 | johsnAge = database ^. at 1 . _Just . age
   |  
2 Answers

You need to make it this:

johsnAge = database ^? at 1 . _Just . age

Or for more brevity,

johnsAge = database ^? ix 1 . age

The problem is that there is no guarantee that the database table will contain the value requested, so both of these expressions are of type Traversal' (Map Int Person) Int. So in this case johnsAge :: Maybe Int.

If you use ^. to access a Traversal and the final value (in this case age) is a Monoid then you get the zero value, and if there are multiple hits (not possible for a Map) then you get their concatenation. However in this case its not a monoid, so you get a type error instead.

Can you use ^? and ix instead?

*Q65578938> database ^? ix 1 . age
Just 35
*Q65578938> database ^? ix 2 . age
Nothing
Related