Reputation: 1852
For example, here is the sum type
data Fruit = Apple Int Color Date
| Banana Color Date
The Date
is purchase date but it sits in different location
for Apple
and Banana
. I'm able to make Prism via makePrisms ''Fruit
Is it possible to create a Lens like:
purchaseDate :: Lens Fruit Date
Then I can easily get/set/modify
on purchase date for all fruits ( both Apple and Banana)?
I think it is different with How can I write a lens for a sum type as the field location is different ¨Date" is in different locations of value constructors
Upvotes: 0
Views: 83
Reputation: 152682
It's pretty mechanical to write lenses; that's why makeLenses
exists. But if makeLenses
doesn't quite do the thing you want, then of course you're free to write your favorite variation by hand!
purchaseDate :: Lens' Fruit Date
purchaseDate f = \case
Apple i c d -> Apple i c <$> f d
Banana c d -> Banana c <$> f d
The lens for extracting the color is extremely similar, though slightly syntactically noisier for fairly uninteresting reasons.
fruitColor :: Lens' Fruit Color
fruitColor f = \case
Apple i c d -> f c <&> \c' -> Apple i c' d
Banana c d -> f c <&> \c' -> Banana c' d
Upvotes: 4
Reputation: 50819
If you give the fields common names, then makeLenses
will generate appropriate lenses. That is, after:
data Color = Red | Yellow deriving (Show)
newtype Date = Date Int deriving (Show)
data Fruit = Apple { _tree :: Int
, _color :: Color
, _date :: Date }
| Banana { _color :: Color
, _date :: Date }
deriving (Show)
makeLenses ''Fruit
there will be color
and date
lenses:
λ> :t date
date :: Lens' Fruit Date
λ> Apple 1 Red (Date 20241217) ^. date
Date 20241217
λ> Banana Yellow (Date 20250101) ^. date
Date 20250101
For fields that aren't in common, like tree
, they'll be made traversals instead:
λ> :t tree
tree :: Traversal' Fruit Int
Upvotes: 4