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