Rescript-compiler: a curated set of built-in ppx

Created on 19 Oct 2016  路  15Comments  路  Source: rescript-lang/rescript-compiler

taken from https://github.com/bloomberg/bucklescript/issues/811

type t  = A of int
[@@bs.derive { constructor} ]
(** generated code below *)
let a v = A v
type t = { x : int }
[@@bs.derive { projector }]
(** generated code below *)
let x (v : t ) = v.x

this is also useful for FFI when we export data types to JS

problem: name clashing:

type u = 
| A of int 
[@@bs.derive{constructor}]
type v = | A of int | B of int
[@@bs.derive{constructor}]

cc @OvermindDL1

HIGH

Most helpful comment

If only OCaml had a way to naturally and natively curry variant constructors (implicit fun generation wrapper or so?)

there is https://github.com/janestreet/ppx_variants_conv why create [@@bs.derive { constructor} ]?

All 15 comments

Name clashing I am fine with if it goes the method of how ppx_fields_conv and ppx_variants_conv go (especially as you know the names are being generated when you yourself put in the deriving call attribute). Especially in 'elm' it is already expected to name things properly to avoid clashes already, plus if someone needs they can always wrap their record/variant up in a sub module as t. The Fields/Variants submodules they make is also highly useful, I especially love the accessor functions as they catch many ways of forgetting to change all usage locations when the structure shape is changed.

If only OCaml had a way to naturally and natively curry variant constructors (implicit fun generation wrapper or so?)

A base ppx like ppx_jane would be nicely all-inclusive and much of it very useful, but is a bit excessive too it really feels like...

As for name clashing, what about this:

If the main type name is t, generate the functions in the same module.

If the main type name is not-t then generate the functions in the same module prepended with _ and also make a module of the same name as the type name with the functions in that for easy let open blah in usage at-location?

If only OCaml had a way to naturally and natively curry variant constructors (implicit fun generation wrapper or so?)

there is https://github.com/janestreet/ppx_variants_conv why create [@@bs.derive { constructor} ]?

@little-arhat we need committed windows support, and it should not be too hard to make it work with trunk compiler. Internally we follow the trunk brunch of ocaml compiler, ppx from Jane is _huge_, I am not sure it is possible to keep it in sync with trunk

@OvermindDL1 I implemented it on #872 , I would love to have your feedback before documenting it(and some clean up).
Examples: see jscomp/test/derive_project_test.ml
We appreciate early adopters' feedback, thanks!

@bobzhang Initial look looks good. I like the unified call for both variants and records.

I'm currently cloning/building the latest master since I see it was just merged, will be doing tests...
(Oh I love how much faster it builds on Linux, I really hate using Windows at work...)

  1. I'm curious about the {ffi} part of the [@@bs.deriving {ffi}] call, is it necessary? If left out is it still usable in OCaml but not via the javascript interface? Or are more ffi style exports planned over time and will use this as well?
  2. Found one oddity, given type msg = NewContent of string [@@bs.deriving {ffi}] it generates a newcontent instead of a newContent as I would expect. Is it supposed to lower-case the entire name instead of just the first character?
  3. Record accessors functions work wonderfully and the middle characters stay properly capitalized as expected as in type model = { dieFace : int } [@@bs.deriving {ffi}] makes a dieFace function.
  4. Ah if only some way to use the variant constructor as-is as the function name, first-char-capped and all, probably require more detailed changes though, plus no real need. ^.^
  5. The rest of the JaneStreet functionality would be nice, but not sure it would be warranted, I think as it is now it seems good as the base setup.

Overall, it works well though the variant constructors should only have the first character lower-cased instead of the entire word, makes things like NewContent -> newcontent be odd when one would expect -> newContent. :-)

thanks for bug report, fixed in #873

@OvermindDL1 we will have other code generators like [@@bs.deriving {ffi ; json}] do you have other better names than ffi

Hmm, considering its use in this context instead of ffi perhaps something like functions or something might be more descriptive.

What is this json one going to generate? :-)

And saw the commit, awesome, will test again when I get back home. :-)

how about ffi_accessors?
serialize/deserialize to json etc.

Ah, accessors might be a perfect name. It is not necessarily just for ffi though so unsure of the ffi part of the name?

/me is a fan of self-descriptive names

As for the json part, a few corner cases there, like can it handle custom type serialization? What about dates? What format for the dates does it do if so (string, the more accurate object notation, etc...)?

Have you seen how my Json decoders/encodes work in my testing project that mostly copies the elm API? It worked surprisingly well, based off the Js.Json code bucklescript contains (with an added external for Json.stringify). :-)

I don't have time to work on json serialize/deserialize yet, yes, certainly it will allow customization.
It probably will care more about efficiency than readability though

How does elm handle serialize/deserlize, elm does not have deriving, right?

Elm is a very simplified language, no hint of HPT, HKT's, GADT's, anything of the sort. It does a lot of HKT-like stuff via javascript magic though.

It handle Json encoding/decoding by defining a set of type-safe decoders/encoders that can be composed. You eventually pass those encoders/decoders and strings/jsonValues to the proper decode/encode functions and it does the parsing. I replicated the API precisely in bucklescript at https://github.com/OvermindDL1/bucklescript-testing/blob/master/src/tea_json.ml although mine uses function composition internally, though it could probably be done via GADT's as well (I was just trying to fulfill the API first, fix it up later), but it works well enough.

A 'json' deriver I'm curious if it would output a string, a value, or you can choose (the elm api, and thus mine, allows both, because elm treats anything 'opaque' from javascript as a json value that can be decoded into the base json types). I can go into more detail if curious, but it works well and I'd only make minor changes to it if I were to not follow the API.

The one thing, at integration points between elm and javascript (since there is no direct calling) it uses 'port's, which can be subscribed to from javascript or 'push'd into from javascript, and thus also subscribed from elm or can push from elm. In elm you just define the type that you expect it to come from and the compiler generates a decoder or encoder as necessary automatically for that type. That is what would be nice to be able to do in bucklescript (the automatic decoder/encoder generation given a type), but short of my own ppx or embedding it into bucklescript itself (which it does not belong since bucklescript's library has no concept of json encoders/decoders) it would not work, but I'm wondering how I could finagle the usage of a 'json' deriver into fitting in to that style and if I would be able to compose it somehow...

@OvermindDL1 yes, basically we do the same thing but save those boilerplate for you, and we can do _really great optimizations_ to save unnecessary allocations, it is something in my mind but no time to do yet(probably next weekend, who knows : ) .
Just send a pull request #874 for the renaming.
closing this issue now, feel free to leave more feedback here

It is working fantastically, simplified a huge amount of callbacks that are traditional in Elm'y code!

Was this page helpful?
0 / 5 - 0 ratings