-
-
Notifications
You must be signed in to change notification settings - Fork 56
Description
I find it a little frustrating that when I have a sum-of-products data type, I can't easily access the fields by their name. I do understand the technical limitations here, but I've still been thinking about workarounds. One that would suit my purposes pretty well is to have a traversal to a field name.
Motivation and Explanation
Ideally, if I have a data type such as
data Foo
= Bar { a :: Int }
| Baz { a :: Int, b :: Bool }
| Qux { a :: Int, b :: Bool, c :: Char }
deriving (Generic)and I have a value Baz 3 True, then I could do a prism-constructor access of Baz followed by a lens-field access on b. Unfortunately, the prism for Baz has type Prism' Foo (Int, Bool) -- Haskell's lack of anonymous records strikes!
At a user level, I can make data types for all of these, as in:
data Foo = Bar Bar' | Baz Baz' | Qux Qux' deriving Generic
data Bar' = Bar' { a :: Int } deriving Generic
data Baz' = Baz' { a :: Int, b :: Bool } deriving Generic
data Qux' = Qux' { a :: Int, b :: Bool, c :: Char } deriving GenericNot only is this some annoying type bloat and makes data construction more irritating, but this creates the problem that I no longer have the free lens field @"a" :: Lens' Foo Int.
I could also define, for instance,
baz_b :: Prism' Foo Bool
qux_b :: Prism' Foo Bool
qux_c :: Prism' Foo Charwhich is a valid, if verbose, option. Template Haskell could help, especially if there are a lot of fields.
As it turns out, there are many cases where one doesn't need the full power of a prism but still wants access to fields that are not defined in every constructor. Obviously, the library maintainer understands this given the traversals in Data.Generics.Product.Types and Data.Generics.Product.Param. I propose this be extended so that one can get a traversal from just a field name. I imagine something like:
class HasFields (field :: Symbol) s t a b where
fields :: Traversal s t a bThis would be very similar to HasField except that it would construct a traversal rather than a lens, and it would not check to make sure that the field is present in every constructor. I think, as good form, it should probably make sure that the field is present in at least one constructor, but technically, this wouldn't be necessary.
For Your Consideration
- Is this a good idea?
- Is there an alternative I should consider?
- How much work do you think this is?
- Would you welcome a PR if I made one?