generic-lens
generic-lens copied to clipboard
Allow data constructor argument as subtype
It would be nice if the following would work. I was surprised that it didn't.
>>> data Dog = MkDog deriving (Generic, Show)
>>> data Cat = MkCat deriving (Generic, Show)
>>> data Duck = MkDuck deriving (Generic, Show)
>>> data Animal = ADog Dog | ACat Cat | ADuck Duck deriving (Generic, Show)
>>> injectSub MkDog :: Animal
<interactive>:7:1: error:
• Couldn't match type ‘'[Duck]’ with ‘'[]’
arising from a functional dependency between:
constraint ‘GIsList (K1 R Duck) (K1 R Duck) '[] '[]’
arising from a use of ‘injectSub’
instance ‘GIsList (Rec0 a) (Rec0 b) '[a] '[b]’
at /home/scottfleischman/GitHub/kcsongor/generic-lens/src/Data/Generics/Product/Internal/HList.hs:111:10-44
• In the expression: injectSub MkDog :: Animal
In an equation for ‘it’: it = injectSub MkDog :: Animal
It seems to fit the definition:
https://github.com/kcsongor/generic-lens/blob/8527fb2567b35b7a20a15f14ab6fd06ad22a387c/src/Data/Generics/Sum/Subtype.hs#L73-L75
since we can give ADog dog :: Animal
given dog :: Dog
.
Would it be possible to add this functionality? It would be convenient to simply pass Dog
and not have to create a wrapper type data SingleDog = SingleDog Dog
when our subtype only has one option.
The existing constructor-renaming behavior works well for creating other subsets of Animal
like the following.
>>> data DogOrCat = DCDog Dog | DCCat Cat deriving (Generic, Show)
>>> injectSub (DCDog MkDog) :: Animal
ADog MkDog
However, this renaming of constructors gave me a surprising result with the example code from the SubType module.
https://github.com/kcsongor/generic-lens/blob/8527fb2567b35b7a20a15f14ab6fd06ad22a387c/src/Data/Generics/Sum/Subtype.hs#L48-L52
https://github.com/kcsongor/generic-lens/blob/8527fb2567b35b7a20a15f14ab6fd06ad22a387c/src/Data/Generics/Sum/Subtype.hs#L57-L61
> injectSub (MkDog "Snowy" 4) :: Animal
Cat "Snowy" 4
Notice the dog changes into a cat! However, it is understandable given that it is effectively renaming the constructor, giving it a name and an age. Yet still surprising that we changed our Dog to a Cat.
If we added the ability I am suggesting, it would be ambiguous in this case whether we want to keep our Dog or change it to a Cat. I'm not sure if it's worth it to try and resolve the ambiguity by a priority or just to have an error in that case?