justinwoo / purescript-simple-json

A simple Purescript JSON library that uses types automatically
http://purescript-simple-json.readthedocs.io
MIT License
133 stars 45 forks source link

Provide examples of decoding recursive types #86

Closed burleight closed 2 years ago

burleight commented 2 years ago

I would like to decode some arbitrarily deep trees with this library.

Should I be using the Generics-Rep approach outlined in the documentation?

data Foo = Foo {
    bar :: Maybe Bar
}

data Bar = Bar {
    foo :: Maybe Foo
}

derive instance genericFoo :: GR.Generic Foo _
derive instance genericBar :: GR.Generic Bar _

instance readForeignFoo :: JSON.ReadForeign Foo where
    readImpl f = GR.to <$> ?help f

-- continue to fill in the implementation holes for readForeignFoo and define a readForeignBar

Could this use case be added to the documentation?

burleight commented 2 years ago

Generics-Rep works.

module Data.JSON.MutualRecursion where

import Prelude

import Data.Maybe (Maybe)
import Data.Generic.Rep as GR
import Simple.JSON as JSON
import Foreign as Foreign
import Foreign (Foreign, MultipleErrors)
import Data.Either (Either)
import Data.Show.Generic (genericShow)

data Foo = Foo {
    bar :: Maybe Bar
}

data Bar = Bar {
    foo :: Maybe Foo,
    x :: Int
}

derive instance genericFoo :: GR.Generic Foo _
derive instance genericBar :: GR.Generic Bar _

derive instance eqFoo :: Eq Foo

instance showFoo :: Show Foo where
    show x = genericShow x

instance showBar :: Show Bar where
    show x = genericShow x

derive instance eqBar :: Eq Bar

instance readForeignFoo :: JSON.ReadForeign Foo where
    readImpl f = GR.to <$> fooRep f

class FooRep rep where
    fooRep :: Foreign -> Foreign.F rep

instance fooConstructor :: (FooRep a) => FooRep (GR.Constructor name a) where
    fooRep f = GR.Constructor <$> fooRep f

instance fooArgument :: (JSON.ReadForeign a) => FooRep (GR.Argument a) where
    fooRep f = GR.Argument <$> JSON.readImpl f

instance readForeignBar :: JSON.ReadForeign Bar where
    readImpl f = GR.to <$> barRep f

class BarRep rep where
    barRep :: Foreign -> Foreign.F rep

instance barConstructor :: (BarRep a) => BarRep (GR.Constructor name a) where
    barRep f = GR.Constructor <$> barRep f

instance barArgument :: (JSON.ReadForeign a) => BarRep (GR.Argument a) where
    barRep f = GR.Argument <$> JSON.readImpl f

readFoo :: String -> Either MultipleErrors Foo
readFoo = JSON.readJSON
justinwoo commented 2 years ago

do you know about newtypes? i think you might want to read about them

burleight commented 2 years ago

Thank you.

The newtype implementation is much simpler - mapping the type constructor onto readImpl