haskell - Automatic derivation of ToJSON for (Map NewtypeOfText v) -
haskell - Automatic derivation of ToJSON for (Map NewtypeOfText v) -
i have map key newtype of text. automatically (as much possible) derive tojson , fromjson map. aeson has instances tojson , fromjson map text v.
my verbose code works:
{-# language derivegeneric #-} module test import classyprelude import data.aeson import ghc.generics (generic) import qualified data.map m newtype mytext = mytext {unmytext::text} deriving (eq, ord) info bar = bar deriving (generic) instance tojson bar instance fromjson bar info foo = foo (map mytext bar) instance tojson foo tojson (foo x) = tojson mp mp = m.fromlist . map (\(x,y) -> (unmytext x,y)) . m.tolist $ x instance fromjson foo parsejson v = convert <$> parsejson v convert :: map text bar -> foo convert = foo . mapfromlist . map (\(x,y) -> (mytext x,y)) . maptolist can more following?
data foo = foo (map mytext bar) deriving (generic) instance tojson foo instance fromjson foo edit i tried (but still no luck):
newtype mytext = mytext {unmytext::text} deriving (eq, ord, tojson, fromjson) instance tojson foo tojson (foo x) = tojson x and
newtype mytext = mytext {unmytext::text} deriving (eq, ord, tojson, fromjson) instance tojson foo
the fact can't automatically derive instance 100% right behavior. reason expect doesn't work there no way know instance fromjson (map text v) can used on values of type map mytext v. because creation , manipulation of map predicated on ord instance of key, , there no way (for compiler) know x y (x == y) == (mytext x == mytext y), required safely coerce map text v map mytext v. more technically, role declaration of map is:
type role map nominal representational essentially says map k v coercible other maps first type parameter identical. wiki says:
we have instance coercible b => coercible (t a) (t b) if , if first parameter has representational role.
the class coercible used type safe coercions in recent versions of ghc (7.8?) more info type roles , role in type safety, see here.
if plan derive instance ord mytext, indeed safe coerce map text v map mytext v, since ord instance same. requires utilize of unsafecoerce. still have write instance yourself, though:
instance tojson v => tojson (map mytext v) tojson = tojson . (unsafecoerce :: map mytext v -> map text v) instance fromjson v => fromjson (map mytext v) parsejson = (unsafecoerce :: parser (map text v) -> parser (map mytext v)) . parsejson if plan write own ord instance, above absolutely not safe. solution correct, not efficient. utilize following:
tojson = tojson . m.mapkeys (coerce :: mytext -> text) parsejson = fmap (m.mapkeys (coerce :: text -> mytext)) . parsejson depending on ord instance, may able utilize mapkeysmonotonic instead, more efficient. see documentation of data.map exactly when can utilize mapkeysmonotonic.
then, obvious things work:
data bar = bar deriving (eq, ord, generic) instance tojson bar instance fromjson bar info foo = foo (map mytext bar) deriving (generic) instance tojson foo instance fromjson foo -- using generalizednewtypederiving newtype foo2 = foo2 (map mytext bar) deriving (fromjson, tojson) full code:
{-# language derivegeneric, generalizednewtypederiving, flexibleinstances #-} module test import data.aeson import ghc.generics (generic) import qualified data.map m import data.map (map) import data.text (text) import ghc.prim (coerce) import unsafe.coerce (unsafecoerce) import data.aeson.types (parser) newtype mytext = mytext {unmytext::text} deriving (eq, ord, generic, tojson, fromjson) instance tojson v => tojson (map mytext v) -- tojson = tojson . m.mapkeys (coerce :: mytext -> text) tojson = tojson . (unsafecoerce :: map mytext v -> map text v) instance fromjson v => fromjson (map mytext v) -- parsejson x = fmap (m.mapkeys (coerce :: text -> mytext)) (parsejson x) parsejson x = (unsafecoerce :: parser (map text v) -> parser (map mytext v)) (parsejson x) info bar = bar deriving (eq, ord, generic) instance tojson bar instance fromjson bar info foo = foo (map mytext bar) deriving (generic) instance tojson foo instance fromjson foo newtype foo2 = foo2 (map mytext bar) deriving (fromjson, tojson) haskell aeson
Comments
Post a Comment