Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

easily encode/decode records #5

Closed
cdepillabout opened this issue Oct 23, 2015 · 8 comments
Closed

easily encode/decode records #5

cdepillabout opened this issue Oct 23, 2015 · 8 comments

Comments

@cdepillabout
Copy link
Member

Is there a way to easily encode and decode records?

For instance, I have the following type:

newtype Register = Register { email :: String
                            , password :: String
                            }

I'd like to use purescript-generics for the encoding and decoding like below:

derive instance genericRegister :: Generic Register
instance encodeJsonRegister :: EncodeJson Register where
    encodeJson = gEncodeJson

But when running the following code:

let register = Register { email: "email@email.com", password: "foobar" }
in show $ encodeJson res

I get the following output: {"values":[{"password":"foobar","email":"email@email.com"}],"tag":"Register"}
I was expecting the output to look like this: {"password":"foobar","email":"email@email.com"}

I tried to change the EncodeJson instance like the following:

instance encodeJsonRegister :: EncodeJson Register where
    encodeJson (Register reg) = gEncodeJson reg

But that just gave me the following compiler error:

Error found:
Error in value declaration encodeJsonRegister:
Error at /opt/src/src/Main.purs line 44, column 1 - line 46, column 1:
Error in module Main:
No instance found for

  Data.Generic.Generic { password :: String
                       , email :: String
                       }

The error makes sense, but I was hoping it would be easier to define encodeJson for records. Is there an easy way? I don't want to have to write out something like this for every type:

  instance encodeJsonRegister :: EncodeJson Register where
    encodeJson (Register reg)
      =  "email" := reg.email
      ~> "password" := reg.password
      ~> jsonEmptyObject

Maybe I've just been spoiled by aeson :-(

@zudov
Copy link
Contributor

zudov commented Oct 23, 2015

Perhaps we could add a special case in gEncodeJSON/gDecodeJSON for ADTs with single constructors and single argument, so that they would be simply encoding/decoding that argument.

How does that sound?

@zudov
Copy link
Contributor

zudov commented Oct 23, 2015

At the moment gEncodeJson/gDecodeJson is just a direct, unambiguous, and very obvious (though a bit too much structured) mapping between GenericSpine and JSON.

To be honest, I didn't really intend it to be used as something "human consumable", the primary target are the cases where you just need an isomorphic serialization/deserialization (like if you want to save something to localstorage).

Making an exception for newtypes would break that property, and gDecodeJSON would become ambiguous.

For example if you have:

newtype Natural = Natural Int

You probably don't want gDecodeJson "-55" :: Either String Natural to return you Right (Natural (-55)).

@cdepillabout
Copy link
Member Author

I guess I'll just write my own version of gEncodeJSON.

It would be nice to have a function similar to deriveJSON that could take options on how to do the encoding/decoding.

Then you would be able to write something like this:

myOpts :: ArgonautCodecOptions
myOpts ...

derive instance genericRegister :: Generic Register
instance encodeJsonRegister :: EncodeJson Register where
    encodeJson = encodeJsonWithOptions myOpts
instance decodeJsonRegister :: DecodeJson Register where
    decodeJson = decodeJsonWithOptions myOpts

@eskimor
Copy link

eskimor commented Jan 25, 2016

I am currently working on genericEncodeJson which takes options like Haskell's aeson does. I plan to file a pull request later in the evening. Unwrapping newtype like data is currently not supported, but it should be easy to add.

@eskimor
Copy link

eskimor commented Jan 26, 2016

Ok, after further investigation, this modified example from @cdepillabout does not work:

newtype Register = Register RegisterImpl

type RegisterImpl = { email :: String
                    , password :: String
                    }

-- instance genericRegisterImpl :: Generic RegisterImpl
instance encodeJsonRegister :: EncodeJson Register where
    encodeJson (Register reg) = gEncodeJson reg

Nor does it work, if you add:

instance genericRegisterImpl :: Generic RegisterImpl

or

instance genericRegisterImpl :: Generic Object ( email :: String , password :: String )

this seems to be related to:

purescript/purescript#1822

I was not aware of this problem, ok I think I have to implement the "unwrapUnaryRecords" option.

@eskimor
Copy link

eskimor commented Jun 23, 2016

unwrapUnaryRecords implemented here.

@texastoland
Copy link

texastoland commented Aug 5, 2016

Can this be closed? It doesn't serve the use case of a type having all *code-able non-Generic fields however.

@cdepillabout
Copy link
Member Author

cdepillabout commented Aug 6, 2016

Yes, I believe this can be considered fixed with the unwrapUnaryRecords option.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants