Closed mrmurphy closed 5 years ago
Ah, if I make my own module and use [@decco.codec] on the type declaration it works:
--- DeccoExt.re
module Date = {
let encoder: Decco.encoder(Js.Date.t) =
date => Js.Date.toISOString(date)->Decco.stringToJson;
let decoder: Decco.decoder(Js.Date.t) =
json => {
switch (Decco.stringFromJson(json)) {
| Result.Ok(v) => Js.Date.fromString(v)->Ok
| Result.Error(_) as err => err
};
};
let codec: Decco.codec(Js.Date.t) = (encoder, decoder);
[@decco]
type t = [@decco.codec codec] Js.Date.t;
};
--- Other File:
[@decco]
type t = {
kind: string,
name: string,
modified: DeccoExt.Date.t,
id: string,
};
Kind of a bummer that I have to use the type alias though, instead of a normal Js.Date.t, no?
The problem is that the annotation needs to go before the field type rather than before the name of the field:
[@decco]
type t = {
kind: string,
name: string,
modified: [@decco.codec DeccoExt.Date.codec] Js.Date.t,
id: string,
};
I'll leave this open as a reminder to call this out more explicitly in the docs.
Riiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiight okay. Thanks!
I just ran into the need for a Dict. I see that's an outstanding issue. https://github.com/ryb73/ppx_decco/issues/4
I thought I'd just use a custom decoder in the mean-time, but I realized that dict is a higher-order decoder, taking a sub-decoder like list and array do. Is there a way to use [@decco.codec]
and provide a higher-order decoder? the type signatures aren't quite matching up for me, since the codec for a dict would roughly be ((encoder, a) => json, (decoder, json) => result(a))
I tried dropping the type signatures to see if I could make the encoder the same way Decco
does for its list and array types:
module Dict = {
let encoder = Json.Encode.dict;
let decoder = (insideDecoder, json) => {
switch (Json.Decode.dict(insideDecoder, json)) {
| exception (Json.Decode.DecodeError(e)) =>
Result.Error({Decco.path: "unavailable", message: e, value: json})
| v => Result.Ok(v)
};
};
let codec = (encoder, decoder);
[@decco]
type t('a) = [@decco.codec codec] Js.Dict.t('a);
};
But I get the cryptic type error:
[27/162] Building src/DeccoExt.cmj
We've found a bug for you!
(No file name)
This expression has type Js.Json.t
It is not a function.
@ryb73 any thoughts on writing custom implementations for higher-order decoders? I just ran into this again, trying to add support for a non-empty list implementation in my decco type. Is it currently possible? Or would it require some serious code changes?
Hey @mrmurphy, sorry for the delay.
I just tried the Dict example that you posted and it actually did work for me. Can you try it again and let me know if there are still problems? Maybe it accidentally got fixed 😄
As a side note, I've never seen that exception
syntax and am curious how it works – do you know if there's documentation on that somewhere?
Re: the discussion earlier in the thread, I added a section to the readme which describes the attributes that decco recognizes. I understand that it's confusing that sometimes the [@decco.<xyz>]
attribute comes before the record field name and sometimes comes after. I'm hoping that with the documentation I added it's more clear why that's the case.
Finally, I realized that you're the host of Reason Town. I love the podcast, I hope you guys keep it up!
Welp, I think I figured out my problem. In the case of the Dict example above, and all other times I've tried this, I left off the type declarations, and added an extra argument to the decoder for the inner decoder, but I forgot to add an extra argument for the inner encoder. Once I added an extra arg to both the encoder and the decoder, the compiler was happy with me again.
For example:
module Loadable = {
type _t('a) =
| NotLoaded
| Loaded('a);
let encoder = (_, _) => Obj.magic(Js.Undefined.empty);
let decoder = (_, _) => Result.Ok(NotLoaded);
let codec = (encoder, decoder);
[@decco]
type t('a) = [@decco.codec codec] _t('a);
};
I'd like to decode a JSON string into a date, so I've used
[@decco.codec ...]
and passed in a custom decoder I've written:But I get a compiler error that "Js.Date.t_encode" does not exist. It seems like the part where I specify the codec isn't working? In order to get around this, I've got to create aliases at the top of the file:
Am I doing something wrong with @decco.codec?