Rescript-compiler: Allow to combine bs.new with more attrs

Created on 19 Mar 2018  路  9Comments  路  Source: rescript-lang/rescript-compiler

There was a conversation in Discord about some bindings to a JS library:

var grpc = require('grpc');
var hello_proto = grpc.load(PROTO_PATH).helloworld;
var client = new hello_proto.Greeter('localhost:50051',...);

Because bs.new can only be combined with bs.module (but in this case, it'd be needed with bs.send) I believe there is no way to express this in BuckleScript (or i havent' been able to find it).

cc @thangngoc89.

discussion enhancement

Most helpful comment

This is what I ended up doing:

let newHack: a => b => t =
    [%bs.raw {|(Constructor, arg) => new Constructor(arg)|}];

Where a, b, and t are your constructor, argument, and output types, respectively. It'd be great to see a native solution for this. I'm also curious to see an answer to @dvisztempacct's question as I share the same confusion.

All 9 comments

It is something we did not support yet: new a dynamic constructor.

I feel it is very hard to type it statically, any proposal is welcome

Some rough ideas:
introduce an abstract type, e.g, ('a0,'a1,'b)Js.constructor

external constructor1 : ('a0, 'b) Js.constructor -> 'a0  -> 'b = "#new"
external constructor2 : ('a0, 'a1, 'b) Js.constructor2  -> 'a0 -> 'a1 -> 'b = "#new"
external constructor3 : ('a0, 'a1, 'a2, 'b) Js.constructor3 -> 'a0 -> 'a1 -> 'a2  -> 'b = "#new"

@bobzhang Is that something being considered for the future? I am trying to figure out the best way to use gRPC from Reason/OCaml.

I feel it is very hard to type it statically, any proposal is welcome

@bobzhang how does @bs.new currently affect the type of the binding? I would imagine that it doesn't affect it at all (you type it however you want when you declare it) and the @bs.new only affects how a Javascript function call is emitted by the back-end.

Perhaps I am misunderstanding...

Is there any way by which this can be achieved using Bucklescript, howsoever hacky it may be?

var client = new hello_proto.Greeter('localhost:50051',...);

A better solution in this particular case would be to write a gRPC compiler that targets OCaml/Reason. https://github.com/mransan/ocaml-protoc is a good head start but it doesn't support services ( https://github.com/mransan/ocaml-protoc/issues/126 ).

@yawaramin that isn't a terrible idea, however I believe it's OT for this issue. At the time, it seemed like the right idea to provide bindings to grpc-node

This is a similar kind of dynamism to #2765 , we can do a similar hacky workaround for the time being. E.g. translating Javier's code,

/* Grpc.re */
type t;
[@bs.module "grpc"] external load: string => t;

/* Greeter.re */
let _helloProto = Grpc.load("/proto/path");
 /* ^ this... */

type t;
[@bs.new] external make: string => t = "_helloProto.helloworld.Greeter";
     /* ...needs to be the same as this ^ */

This again relies on BuckleScript not mangling names, and is unsafe, but no more so than [%%bs.raw] and the unsafety can be wrapped up in a module like above.

This is what I ended up doing:

let newHack: a => b => t =
    [%bs.raw {|(Constructor, arg) => new Constructor(arg)|}];

Where a, b, and t are your constructor, argument, and output types, respectively. It'd be great to see a native solution for this. I'm also curious to see an answer to @dvisztempacct's question as I share the same confusion.

Was this page helpful?
0 / 5 - 0 ratings