haskell-graphql / graphql-api

Write type-safe GraphQL services in Haskell
BSD 3-Clause "New" or "Revised" License
406 stars 35 forks source link

genericFromValue fails for a record with four or more fields #173

Closed harendra-kumar closed 6 years ago

harendra-kumar commented 6 years ago

For this record I cannot derive a FromValue instance:

data Resource = Resource
    { roleId      :: Text
    , resAge      :: Int32
    , resGender   :: Text
    , resLang     :: Text
    } deriving (Generic, Show)

It works if I remove the last field i.e. resLang otherwise it produces the following error:

    • No instance for (GraphQL.Internal.Value.FromValue.GenericFromValue
                         ((GHC.Generics.S1
                             ('GHC.Generics.MetaSel
                                ('Just "roleId")
                                'GHC.Generics.NoSourceUnpackedness
                                'GHC.Generics.NoSourceStrictness
                                'GHC.Generics.DecidedLazy)
                             (GHC.Generics.Rec0 Text)
                           GHC.Generics.:*: GHC.Generics.S1
                                              ('GHC.Generics.MetaSel
                                                 ('Just "resAge")
                                                 'GHC.Generics.NoSourceUnpackedness
                                                 'GHC.Generics.NoSourceStrictness
                                                 'GHC.Generics.DecidedLazy)
                                              (GHC.Generics.Rec0 Int32))
                          GHC.Generics.:*: (GHC.Generics.S1
                                              ('GHC.Generics.MetaSel
                                                 ('Just "resGender")
                                                 'GHC.Generics.NoSourceUnpackedness
                                                 'GHC.Generics.NoSourceStrictness
                                                 'GHC.Generics.DecidedLazy)
                                              (GHC.Generics.Rec0 Text)
                                            GHC.Generics.:*: GHC.Generics.S1
                                                               ('GHC.Generics.MetaSel
                                                                  ('Just "resLang")
                                                                  'GHC.Generics.NoSourceUnpackedness
                                                                  'GHC.Generics.NoSourceStrictness
                                                                  'GHC.Generics.DecidedLazy)
                                                               (GHC.Generics.Rec0 Text))))
        arising from a use of ‘GraphQL.Internal.Value.FromValue.$dmfromValue’
    • In the expression:
        GraphQL.Internal.Value.FromValue.$dmfromValue @Resource
      In an equation for ‘GraphQL.Internal.Value.FromValue.fromValue’:
          GraphQL.Internal.Value.FromValue.fromValue
            = GraphQL.Internal.Value.FromValue.$dmfromValue @Resource
      In the instance declaration for ‘FromValue Resource’
    |
359 | instance FromValue Resource
    |          ^^^^^^^^^^^^^^^^^^

Any idea what the problem might be?

harendra-kumar commented 6 years ago

This is a serious impediment/bug in using this library, does anyone have any ideas about the problem or how to fix it?

jamesdabbs commented 6 years ago

@harendra-kumar - while it looks like PR #178 has the "right" fix for this, in the mean time, you can always write your HasAnnotatedType instances by hand: see e.g. https://github.com/pi-base/server/blob/master/src/Graph/Class.hs#L166 / https://github.com/pi-base/server/blob/master/src/Graph/Class.hs#L246 for a reference.

harendra-kumar commented 6 years ago

That's great, thanks @jamesdabbs !

EdmundsEcho commented 6 years ago

Gabriel Gonzalez had a similar problem with his optparse-generic package. He posted a detailed explanation of the problem that I thought might be helpful here.

-- START Alright, I also fixed the issue of parsing 4 or more alternatives in c6e845c.

Here's a post-mortem explaining what went wrong for those who are curious. The correct version should have four instances:

-- Instance #1
instance
        (GenericParseRecord (f :+: g), GenericParseRecord (h :+: i))
    =>  GenericParseRecord ((f :+: g) :+: (h :+: i))

-- Instance #2
instance
        (Constructor c, GenericParseRecord f, GenericParseRecord (g :+: h))
    =>  GenericParseRecord (M1 C c f :+: (g :+: h))

-- Instance #3
instance
        (Constructor c, GenericParseRecord (f :+: g), GenericParseRecord h)
    =>  GenericParseRecord ((f :+: g) :+: M1 C c h)

-- Instance #4
instance
        (Constructor c1, Constructor c2, GenericParseRecord f1, GenericParseRecord f2)
    =>  GenericParseRecord (M1 C c1 f1 :+: M1 C c2 f2) where

The first problem was that instances 2 and 3 had an infinite loop, and they get triggered when you have three or more constructors. That's why you got an infinite loop when you had three alternatives

The second problem was that instance 1 was missing and was necessary for data types with 4+ alternatives. That is why the code failed to type-check for 4 or more alternatives.

-- END

Here is the link to the full post and code repo.

- E