composite
composite copied to clipboard
Documentation: how do you decode into a record?
for example, the equivalent of aeson's eitherDeode
.
ah good point. probably each module should grow its own README with some more explanation.
in the interim though, you do this via aeson-better-errors
, so following the README example it'd be something like:
import Composite.Aeson (fromJsonWithFormat)
import Composite.Record (Record, Rec((:&), RNil), val)
import Data.Aeson.BetterErrors (ParseError, parseValue)
import Data.Aeson.QQ (aesonQQ)
eitherParseErrorOrAlice :: Either (ParseError e) (Record User)
eitherParseErrorOrAlice =
parseValue (fromJsonWithFormat userFormat)
[aesonQQ|{"id": 1, "name": "Alice"}|] -- val @"id" 1 :& val @"name" "Alice" :& RNil
thanks!
Yeah, I can add that to the docs, and any other examples. This package is awesome.
On Nov 19, 2017 11:31 AM, "Ross MacLeod" [email protected] wrote:
ah good point. probably each module should grow its own README with some more explanation.
in the interim though, you do this via aeson-better-errors, so following the README example it'd be something like:
import Composite.Aeson (fromJsonWithFormat)import Composite.Record (import Data.Aeson.BetterErrors (ParseError, parseValue)import Data.Aeson.QQ (aesonQQ) eitherParseErrorOrAlice :: Either (ParseError e) (Record User) eitherParseErrorOrAlice = parseValue (fromJsonWithFormat userFormat) [aesonQQ|{"id": 1, "name": "Alice"}|] -- val @"id" 1 :& val @"name" "Alice" :& RNil
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ConferHealth/composite/issues/13#issuecomment-345542794, or mute the thread https://github.com/notifications/unsubscribe-auth/ACNoMYpnvDEgxVJkrPQTB6fTFa7VfmPqks5s4IIBgaJpZM4QjfSc .
double thanks! any help is of course appreciated! glad you're enjoying it so far, please do let us know if you have any improvements you think are good to have or other feedback
also, how do nested records work? for example,
type CardRecord = Record Card
type Card =
[ "name" ::: Text
, "legalities" ::: List (Record [ "format" ::: Text
, "legality" ::: Text
])
]
type LegalityRecord = Record Legality
type Legality =
[ "format" ::: Text
, "legality" ::: Text
]
-- using a type alias btw,
type (:::) = (:->)
type List = ([])
I understand the but it's the wrong kind, but why does DefaultJsonFormatRecord
take the raw type level list, instead of the record or a proxy-record? my schema has several nested records, and I'd like to derive a formatting to initially explore the json. and relatedly, I can't construct a schema type that doesn't mention records, the Card
in this example, because normal type constructors like Maybe
and []
need *
, not [*]
(since the inner record, Legality
in this example, is wrapped with another type constructor, I can't just inline its fields). or is it expected to manually construct the formatting for these schemas?
nested record encoding is a bit annoying at present because we don't want to force all records to have DefaultJsonFormat
typed field values, and instancing DefaultJsonFormat
for whatever your particular record type requires funny instance resolution extensions. We typically either use newtypes or explicitly format the subrecord.
newtypes:
data Legality = LegalityBanned | LegalityRestricted | LegalityLegal
deriving (Bounded, Eq, Generic, Ord, Show)
legalityJsonFormat :: JsonFormat e Legality
legalityJsonFormat = enumJsonFormat "Legality"
instance DefaultJsonFormat Legality where defaultJsonFormat = legalityJsonFormat
type LegalityForFormat = '[ "format" :-> Text, "legality" :-> Legality ]
makeRecordJsonWrapper "LegalityForFormatJson" ''LegalityForFormat
type Card = '[ "name" :-> Text, "legalities" :-> [LegalityForFormatJson] ]
makeRecordJsonWrapper "CardJson" ''Card
pro: fairly easy to do, con: "infects" your data with json-ness, you need to navigate the wrappers
explicit formats:
data Legality = LegalityBanned | LegalityRestricted | LegalityLegal
deriving (Bounded, Eq, Generic, Ord, Show)
legalityJsonFormat :: JsonFormat e Legality
legalityJsonFormat = enumJsonFormat "Legality"
instance DefaultJsonFormat Legality where defaultJsonFormat = legalityJsonFormat
type LegalityForFormat = '[ "format" :-> Text, "legality" :-> Legality ]
type Card = '[ "name" :-> Text, "legalities" :-> [Record LegalityForFormat] ]
cardJsonFormatRecord :: JsonFormatRecord e Card
cardJsonFormatRecord
= field @"name" defaultJsonFormat
:& field @"legalities" (arrayJsonFormat (recordJsonFormat defaultJsonFormatRecord))
:& RNil
makeRecordJsonWrapperExplicit "CardJson" ''Card [| cardJsonFormatRecord |]
pro: totally separates JSON-ness from data model, can more easily have more than a single canonical encoding for a particular data model, con: more typing/boilerplate
@Dridus How funny are the instance resolution extensions? Could you show a quick example similar to the ones above?
@torgeirsh it's been a while so my mental cache of the concerns here isn't fresh, but I'll think about it and try to get back to you. my vague recollection is that you'd need IncoherentInstances to define the instances it's looking for, since you're defining them for types you didn't define here