Search code examples
haskellhaskell-lens

Use Haskell's lenses library to fmap a lens


In the code below, my question concerns the top-most function someFunc (everything below is just to provide a complete example). I use a record-syntax getter and fmap there. What's the lens way to implement someFunc?

import           Control.Lens
import           Data.IntMap  (IntMap)

someFunc :: Farm -> IntMap Size
someFunc farm =
  _barnSize <$> farm ^. farmBarns

data Farm = Farm
  { _farmBarns :: IntMap Barn
  }

farmBarns :: Lens' Farm (IntMap Barn)
farmBarns = lens _farmBarns (\farm barns -> farm { _farmBarns = barns } )

type Size = (Int, Int)

data Barn = Barn
  { _barnSize :: Size
  }

barnSize :: Lens' Barn Size
barnSize = lens _barnSize (\barn size -> barn { _barnSize = size } )

Solution

  • Just replace _barnSize by (^. barnSize) or, equivalently, view barnSize:

    someFunc :: Farm -> IntMap Size
    someFunc farm = view barnSize <$> farm ^. farmBarns
    

    For a "100% lens" solution, you can use the mapped setter. In this case, though, I don't think there is any real advantage in doing that.

    someFunc :: Farm -> IntMap Size
    someFunc farm = (mapped %~ view barnSize) (farm ^. farmBarns)
    

    Another possible spelling involves using to to combine everything in a single getter. That doesn't buy you much here either, but it might be somewhat convenient if you wanted to keep working with the IntMap in lens style by chaining additional getters/folds/etc.

    someFunc :: Farm -> IntMap Size
    someFunc farm = farm ^. farmBarns . to (fmap (view barnSize))
    

    There is a special purpose combinator that subsumes the to/(^.) combination above. It is called views:

    someFunc :: Farm -> IntMap Size
    someFunc farm = views farmBarns (fmap (view barnSize)) farm