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.
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
-- ..