Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow data constructor argument as subtype #97

Open
scott-fleischman opened this issue Sep 6, 2019 · 0 comments
Open

Allow data constructor argument as subtype #97

scott-fleischman opened this issue Sep 6, 2019 · 0 comments

Comments

@scott-fleischman
Copy link

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:

-- |Structural subtyping between sums. A sum 'Sub' is a subtype of another sum
-- 'Sup' if a value of 'Sub' can be given (modulo naming of constructors)
-- whenever a value of 'Sup' is expected. In the running example for instance,

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.

-- data Animal
-- = Dog Dog
-- | Cat Name Age
-- | Duck Age
-- deriving (Generic, Show)

-- data Dog = MkDog
-- { name :: Name
-- , age :: Age
-- }
-- deriving (Generic, Show)

> 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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant