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

Generic polymorphic schema handle lists differently to aeson #58

Open
brprice opened this issue Aug 10, 2022 · 2 comments
Open

Generic polymorphic schema handle lists differently to aeson #58

brprice opened this issue Aug 10, 2022 · 2 comments

Comments

@brprice
Copy link

brprice commented Aug 10, 2022

Consider a polymorphic type data T a = T [a], where the parameter is used in a list. The generically-derived instance ToSchema (T Char) agrees with ToJSON (encodes as a string), but the polymorphic version instance ToSchema (T a) does not (when used at a ~ Char -- it encodes as an array of "characters" i.e. length-1 strings.).

A complete example

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}

module Main where

import Data.Aeson
import Data.OpenApi
import GHC.Generics

data Poly a = Poly [a]
 deriving (Generic,Show)

data Mono a = Mono [a]
 deriving (Generic,Show)

instance ToJSON a => ToJSON (Poly a)
instance ToSchema a => ToSchema (Poly a)

instance ToJSON (Mono Char)
instance ToSchema (Mono Char)

main :: IO ()
main = do
  putStrLn "Testing monomorphic instance"
  go $ Mono "foo"
  putStrLn ""
  putStrLn "Testing polymorphic instance"
  go $ Poly "foo"
 where
  go x = do
    case validatePrettyToJSON x of
      Nothing -> putStrLn "validation passed"
      Just err -> putStrLn "validation FAILED" >> putStrLn err

which results in

Testing monomorphic instance
validation passed

Testing polymorphic instance
validation FAILED
Validation against the schema fails:
  * expected JSON value of type OpenApiArray

JSON value:
"foo"

Swagger Schema:
{
    "items": {
        "example": "?",
        "maxLength": 1,
        "minLength": 1,
        "type": "string"
    },
    "type": "array"
}

Swagger Description Context:
{}

This is presumably because openapi3 and aeson handle the special case of [Char] differently.
(There may be some other such cases that differ -- I have not looked further. #50 is perhaps related.)

@maksbotan
Copy link
Collaborator

I fear that once instance ToSchema (Poly a) is generated, there is no way to "look" at concrete instantiations of a. Some horrible magic is required to get it right.

Is this case important for you, or just a peculiarity?

@brprice
Copy link
Author

brprice commented Nov 5, 2022

Is this case important for you, or just a peculiarity?

I ran across this problem in a real project. Thankfully I can work around this bug by listing out the particular monomorphic instances I need.

I fear that once instance ToSchema (Poly a) is generated, there is no way to "look" at concrete instantiations of a. Some horrible magic is required to get it right.

Sure, you cannot "look" in this way. We could (with only a slightly more complex class) generate the correct polymorphic instance in the first place. The way this tends to be done (e.g. aeson and base's Show) is to add an extra member to the class toJSONList :: [a] -> Value, showList :: [a] -> [ShowS] which is then used in the general instance C a => instance C [a]. (Note that this extra member can have a default definition, so there is no extra burden when defining members of the class, unless one wants to handle lists peculiarly).

(Perhaps this is what you meant by "horrible magic"?)

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

2 participants