Encoding of sums of records
kl0tl opened this issue · 0 comments
kl0tl commented
Sums of records are (de)serialized the same by aeson
and this library:
:set -XDeriveGeneric
:set -XDeriveAnyClass
> import Data.Aeson (ToJSON, encode)
> import GHC.Generics (Generic)
> data A = A deriving (Generic, ToJSON)
> data B = B deriving (Generic, ToJSON)
> data D = C1 { f :: A } | C2 { g :: B } deriving (Generic, ToJSON)
> encode $ C1 A
"{\"tag\":\"C1\",\"f\":[]}"
> import Data.Generic.Rep (class Generic)
> import Data.Argonaut (class EncodeJson, encodeJson, stringify)
> import Data.Argonaut.Aeson.Encode.Generic (genericEncodeAeson)
> import Data.Argonaut.Aeson.Options (defaultOptions)
> data A = A
> data B = B
> data D = C1 { f :: A } | C2 { g :: B }
> :paste
… derive instance genericA :: Generic A _
… instance encodeJsonA :: EncodeJson A where
… encodeJson = genericEncodeAeson defaultOptions
… derive instance genericB :: Generic B _
… instance encodeJsonB :: EncodeJson B where
… encodeJson = genericEncodeAeson defaultOptions
… derive instance genericD :: Generic D _
… instance encodeJsonD :: EncodeJson D where
… encodeJson = genericEncodeAeson defaultOptions
… ^D
> stringify $ encodeJson $ C1 { f: A }
"{\"f\":[],\"tag\":\"C1\"}"
but while such data types are perfectly fine in PureScript they are unsafe in Haskell since the declaration of D
yields f :: D -> A
and g :: D -> B
, which are partial.
This can be worked around by wrapping the fields of each constructor in their own type:
data C1Contents = C1Contents { f :: A } deriving (Generic, ToJSON)
data C2Contents = C2Contents { g :: B } deriving (Generic, ToJSON)
data D' = C1' C1Contents | C2' C2Contents deriving (Generic, ToJSON)
but this changes the encoding:
> encode $ C1' $ C1Contents A
"{\"tag\":\"C1'\",\"contents\":{\"f\":[]}}"
I’m aware that we can recover the same encoding in PureScript with newtypes, but would you be open to add to Data.Argonaut.Aeson.Options.SumEncoding a boolean option governing this behaviour?