Binding Labled Arguments To Function That Takes Object

Hello again! Thanks in advance for the comments and help- the forum has been good to me so far!

I have the following bindings to a JS function that takes an object as parameters, like this: foo({ one, two }). I would like to expose labeled arguments to my ReScript code but have them translated as an object in the bindings. Here’s what I have in my bindings:

module App = {
  type t
  @send external start: (t, ~port: int) => Js.Promise.t<unit> = "start"

  @module("@slack/bolt") @new
  external make: (
    ~token: string,
    ~signingSecret: string,
    ~receiver: ExpressReceiver.expressReceiver,
  ) => t = "App"
}

let app = SlackBolt.App.make(
  ~token=Const.slackToken,
  ~signingSecret=Const.slackSigningSecret,
  ~receiver=expressReceiver,
)

That compiles to the following JS code which obviously doesn’t call the JS the way it needs to be called:

var app = new Bolt.App(Const.slackToken, Const.slackSigningSecret, expressReceiver);

I read through the following resources and didn’t see anything- maybe it’s not possible?

That’s a very good question. There’s no built-in way to do it. I covered the best way to achieve something similar in my cookbook, GitHub - yawaramin/bucklescript-bindings-cookbook: Task-oriented guide to writing JavaScript bindings for BuckleScript

2 Likes

There is actually an issue about this, I too think it would be quite useful for bindings!

1 Like

Going to give this a shot, thanks for the link! What’s the current idiomatic approach? Should I just do an object and not worry about labeled arguments?

Thanks for the heads up! I hope it’s not too noisy, but I put a +1 in that thread.

Well, currently, it’s easy to bind in such a way that you end up writing foo(fooOptions(~one, ~two, ())), which I think is not too bad, just a bit verbose. That’s what my cookbook example shows.

2 Likes

Sounds good! I got it working based on your cookbook example, thanks!

1 Like

I also use the foo(fooOptions()) pattern but I sometimes prefer it based on @deriving(abstract), rather than @obj. They do very similar things when used for this purpose, deriving can be a bit unwieldy when every field is optional but it does have the advantage of looking a bit more like the JS code.

https://rescript-lang.org/try?code=C4TwDgpgBAtg1gEwJYCcDyZhIPYDsDOAUAALYBGAVlBAB7AQq4CGANrIqhlnvgFxQAKAH4oIAYwCuKfEgBuEfmWzYWAXgD8AGigTcSYAEooqgHztk6TDgLGoAIjuESs1tToNmbeBf4D8wFCRcAHNtIWwrHn5vTkiCDW1dfSNTHT1gWzsYlEcSBAY5IOCBJjJ-FCYxQ0JQSChsYAALBgBZDktuG1UoAG9CKChcbBQYVgAFJgqYRWUWTX6oUjjXUUlpOQUoJRVCAF9nV1p6RlcG5pQ2n0FyorCIzr56ptb2rmt8BLTk4zMkjO6su1cgB6YFQACq+CYwQUThYEAyAhS5lQAjs+BQYjs2iRBkI8MRyOyaIxYmBoyC2Kg4TiH2ybx4wlWUhk8lUAQkEBxBgM3Lx+IRgmRZxeFhJmPJTEpd1pqhFF1etOEQxG40mTBg7JQnLCzPWbI5XKFvONTiAA

I would happily switch all of these to the proposed @objFn if it were created.

1 Like
@deriving(abstract)
type otherMkdirOptions = {
  normalParam: bool,
  @optional recursive: bool
}

Oooo, I really like that, thanks for the tip!

1 Like

It’s definitely nice. Be careful with the bindings shadowing though, in case you have some normalParam, recursive and other things that share the same name in the scope. Happens more often that you’d think, especially if you have multiple of these @deriving(abstract)

1 Like

yeah the few places we use it the scope is carefully controlled, either specific to the module it’s in or with a nested module :slight_smile:

1 Like