generic-lens icon indicating copy to clipboard operation
generic-lens copied to clipboard

Allow data constructor argument as subtype

Open scott-fleischman opened this issue 5 years ago • 0 comments

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?

scott-fleischman avatar Sep 06 '19 22:09 scott-fleischman