Search code examples
haskellhaskell-lenslenses

A lens for getting or setting a record field determined by a runtime argument


I have these types (and more):

data Player = PlayerOne | PlayerTwo deriving (Eq, Show, Read, Enum, Bounded)

data Point = Love | Fifteen | Thirty deriving (Eq, Show, Read, Enum, Bounded)

data PointsData =
  PointsData { pointsToPlayerOne :: Point, pointsToPlayerTwo :: Point }
  deriving (Eq, Show, Read)

I'm doing the Tennis kata, and as part of the implementation, I'd like to use some functions that enable me to get or set the points for an arbitrary player, only known at runtime.

Formally, I need functions like these:

pointFor :: PointsData -> Player -> Point
pointFor pd PlayerOne = pointsToPlayerOne pd
pointFor pd PlayerTwo = pointsToPlayerTwo pd

pointTo :: PointsData -> Player -> Point -> PointsData
pointTo pd PlayerOne p = pd { pointsToPlayerOne = p }
pointTo pd PlayerTwo p = pd { pointsToPlayerTwo = p }

As demonstrated, my problem isn't that I can't implement these functions.

They do, however, look lens-like to me, so I wonder if I could get that functionality via the lens library?

Most of the lens tutorials show how to get or set a particular, named part of a bigger data structure. This doesn't quite seem to fit what I'm trying to do here; rather, I'm trying to get or set a sub-part determined at runtime.


Solution

  • Based on the answer from Fyodor Soikin and comment from duplode, I ended up using makeLenses from lens and writing a function that returns the appropriate lens:

    data PointsData =
      PointsData { _pointsToPlayerOne :: Point, _pointsToPlayerTwo :: Point }
      deriving (Eq, Show, Read)
    makeLenses ''PointsData
    
    playerPoint :: Player -> Lens' PointsData Point
    playerPoint PlayerOne = pointsToPlayerOne
    playerPoint PlayerTwo = pointsToPlayerTwo
    

    It can be used like this fragment of a bigger function:

    score :: Score -> Player -> Score
    -- ..
    score (Points pd) winner = Points $ pd & playerPoint winner %~ succ
    -- ..