-
Notifications
You must be signed in to change notification settings - Fork 8
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
ReScript support #32
ReScript support #32
Conversation
I'll hold this PR a bit (about 1 month) because I will focus on separate the target-agnostic part of the current implementation as a library:
This refactoring will make it easier to work on ReScript support. |
ReScript difficultiesHard/unable to workaround
Can be workaround, but cumbersome to use
Easy to workaround
|
Many things need to be polished (e.g. use https://gist.github.com/cannorin/4cfd43318e4efe811d7d595a8f2c710b |
So cool!! 😄 👍 |
On hold until rescript-lang/rescript#5364 and rescript-lang/rescript#5368 are merged/addressed. |
14e59ec
to
e9d3937
Compare
I believe that new and variadic have finally landed with the latest ReScript version! |
e9d3937
to
466d6f4
Compare
227aed1
to
6796a30
Compare
... it's compiling with rescript v10! |
@cannorin: Thanks, for your work on this! I'm pretty excited about the possibility of not having to write every single binding by hand anymore! :) I'm wondering: Is this already in a more or less usable state? Currently, I'm trying to use Playwright by creating bindings manually, but there are like a million types (even if I only create bindings for what I use), so something like Do you think, |
@d4h0 It should be possible to parse the type definitions of Playwright and generate a binding that compiles, but I don't think the generated binding would be user-friendly; it's still lacking some important optimizations that affect QoL (e.g. emit I'll fix the broken CI tomorrow or next just in case you want to try it out. |
d2f0da9
to
1cc321a
Compare
a6a4ed9
to
5c86226
Compare
@cannorin |
@zth Thanks! Variant-to-variant coercion will definitely help. |
Thanks for creating this, the generated binding looks great! What is the strategy for TS definitions with lots of 3rd party TS definition dependencies? To build ReScript typing for a React component from TS definition, I had to build my own script on top of npm package managers to fetch all dependencies and manipulate the output file names. Then I found, since the TS definition depends on |
@mxj4 Thank you for trying it out! In general, users may need to modify the generated bindings to make it work with the existing hand-crafted bindings. However, I do think React component packages deserve some special treatment. |
@zth In ts2ocaml, classes and interfaces are represented as abstract types to allow subtyping, field shadowing, and method overloading at the same time (which is impossible if they are defined as record types). Would it be possible to "inline" the arguments of zero-cost-binding variant types to accomplish something like below?: type intf<-'tags>
// interface ShapeLike { kind: string }
module ShapeLike = {
type t = intf<[#Shape]>
type tags = [#Shape]
type this<'tags> = intf<'tags> constraint 'tags = [> #Shape]
@get external kind: this<'tags> => string = "kind"
}
// interface Circle extends ShapeLike { kind: "circle", radius: number }
module Circle = {
type t = intf<[#Circle | ShapeLike.tags]>
type tags = [#Circle | ShapeLike.tags]
type this<'tags> = intf<'tags> constraint 'tags = [> #Circle]
@get external kind: this<'tags> => [#circle] = "kind"
@get external radius: this<'tags> => float = "radius"
@obj external make: (~kind: [#circle], ~radius: float) => t = ""
}
// interface Square extends ShapeLike { kind: "square", sideLength: number }
module Square = {
type t = intf<[#Square | ShapeLike.tags]>
type tags = [#Square | ShapeLike.tags]
type this<'tags> = intf<'tags> constraint 'tags = [> #Square]
@get external kind: this<'tags> => [#square] = "kind"
@get external sideLength: this<'tags> => float = "sideLength"
@obj external make: (~kind: [#square], ~sideLength: float) => t = ""
}
let circle: Circle.t = Circle.make(~kind=#circle, ~radius=4.2)
let circleKind: string = circle->ShapeLike.kind // this works because of subtyping via `intf<-'tags>`
// type Shape = Circle | Square
module Shape = {
@tag("kind")
type t =
| @as("circle") @unsafe_inline Circle(Circle.t)
| @as("square") @unsafe_inline Square(Square.t)
}
let circleShape = Shape.Circle(circle)
// I want this to be compiled to:
//
// var circleShape = circle
let getAreaOfShape = (shape: Shape.t) =>
switch (shape) {
| Shape.Circle(c) => c->Circle.radius *. c->Circle.radius *. Js.Math._PI
| Shape.Square(s) => s->Square.sideLength *. s->Square.sideLength
}
// I want this to be compiled to:
//
// function getAreaOfShape(shape) {
// if (shape.kind === "circle") {
// return shape.radius * shape.radius * Math.PI;
// }
// return shape.sideLength * shape.sideLength;
// } |
Hi @cannorin in your opinion, how long do we have to wait before we can use it? |
@listepo You could use it today if you can build it yourself. As for the official release, I guess it will be around the v11 release of ReScript, as ts2ocaml depends on the new features. |
Rescript 11 is released. Is this package ready now? |
@baku-yaki Thank you for letting me know, I'll make some changes for releasing (since it is currently using a quite old RC version of v11). I think it will be live within a month! |
TODO List:
Future Works:
|
b38424b
to
72d0193
Compare
I have just released the ReScript support as Although I have tested that the generated binding compiles for a variety of NPM packages (namely, Please try it out by |
Related: #166
Adds ReScript as a target.
TODOs:
to_js
andof_js
in gen_js_api)import
/require
on our side (link)Set up automated package publishing(not needed since the stdlib is minimal)Notes:
Subtyping
The "tag" subtyping should look like the following:
In this way, it will allow one-step casting as well as an easy access to its fields:
content
type should only have value fields (no methods = no overloading)(this: t, ...) => ...
rather than(this: content, ...) => ...
Discriminated Unions
A discriminated union should look like this:
This makes it easy to handle union types with almost zero overhead:
Callable & Newable Objects
A callable/newable object should look like this:
Maybe we should generate
.resi
files tooReScript bindings are written in
.res
files, in which recursive modules are hard to write. But we can just generate a flat.res
file and then generate a corresponding.resi
file to hide internal types.