I have several data structures like
data Data1 = Data1
{ _data1Field :: Int
-- More fields
} deriving (Eq, Show)
makeLenses ''Data1
data Data2 = Data2
{ _data2Field :: Int
-- More fields
} deriving (Eq, Show)
makeLenses ''Data2
-- More similar data types
So I decided to write a simple type class to make it easier to compose
class HasField a where
field :: Lens' a Int
instance HasField Data1 where
field = data1Field
instance HasField Data2 where
field = data2Field
But then I ran into the problem that some of these structures have the corresponding field as optional
data Data3 = Data3
{ _data3Field :: Maybe Int
-- More fields
} deriving (Eq, Show)
makeLenses ''Data3
And now I can no longer use the type class. Since there are about the same number of data types that have that field optional as not, I decided that it'd be better to change the typeclass:
class HasField a where
field :: Lens' a (Maybe Int)
instance HasField Data3 where
field = data3Field
But since I'm not very experienced with the lens library, I'm stuck figuring out how to make this new lens work with the types for Data1
and Data2
. Ideally, I'd like to be able to view it and get a Maybe Int
value for any type, and when setting I'd like Just x
to set the field to x
for Data1
and Data2
and be a no-op for those two types when passed Nothing
.
Is this possible using existing combinators or am I going to have to write the lens myself? I'm fine doing so, but the majority of existing tutorials use TH and gloss over the details of writing one by hand.
I'm using GHC 7.6.3 and lens
3.10.
As a follow up to shachaf
class HasFieldA d where
field :: Traversal' d Int
instance HasFieldA Data1 where
field = data1Field -- Lens's are Traversals
instance HasFieldA Data3 where
field = data3Field . _Just
And then the ^?
operator or the ^..
operator
getField :: HasFieldA d => d -> Maybe Int
getField = d ^? field -- or preview field d
to get it.
To set optional fields, you'd need another function
class SetFieldA d where
setField :: Setter' d Int
instance SetFieldA Data3 where
setField = set data3Field . Just