Bindings are one of the most difficult parts for beginners, recently I made some clean up on how externals are handled and think it could be significantly simplified. This comes with two enhancement, the first one is a minor enhancement, the second one is a major one. This proposal should be backwards compatible
First enhancement , no redundant attributes like @val
Previously, for a simple binding, user has to add an attribute, this does not need any more
@val
external getElementById : string => dom = "getElementById"
let h = getElementById("x")
Without @val, this should just work:
external getElementById : string => dom = "getElementById"
let h = getElementById("x")
Second enhancement: we can accept more kinds of payload in the string literals:
User can write any valid JS function inside the string literal, since it is plain JavaScript, so in theory, user can build any bindings without a second thought. Thanks to ES6 syntax, this could be very short. We will do as much check as we can, optimisations can be added later
The second one is also achieved by using raw. What would be the difference between these two approaches?
I guess external would be more readable than raw.
We also want to encourage this style. Based on my observation, users waste too much time in creating a beautiful binding when it can be easily done in a couple of lines of raw code, I consider this as a pre-mature optimization.
Some minor differences:
external can be inlined when it is possible, we may spend more time on it in the future
It is more polymorphic friendly, currently for raw, it can not be polymoprhic
raw makes type annotation optional which is a bad thing, it is recommended to always annotate externals
It will be important to make it clear in the docs what is preferred in terms of the keywords. For example, @module still seems useful since the compiler will emit different kinds of modules depending on configuration, but maybe others like @new should be deemphasized and instead encourage writing the JS in the string.
Will the JS string be parsed and checked like in raw?
These changes would greatly simplify some real life more complex interop, like sgrove posted here. Then he could bind easily with something like this (if I understood correctly):
I’m all for this, @val always seemed a bit weird. I really like the second, I always get weird unused argument errors when I try to do that in a raw block.
I am a new comer to ReScript and i am struggling a bit with binding. It’s good to know that i can forget about one of the binding directives.
And the second one is really great. Looking forward to it.
Thank you for keep improving the language.
Will the JS string be parsed and checked like in raw ?
Yes
These changes would greatly simplify some real life more complex interop, like sgrove posted here .
Yes, pretty much most things you can do, I will optimize some common patterns to make it zero-cost like what we had. The main motivation is to reduce the learning complexity of current various attributes.
well we could have all the DOM document methods in Js.Document and all DOM Window methods in Js.Window so we would call for example setTimeout like Js.Window.setTimeout(()=>Js.log("hi"),1000)
That way there would be no need to write bindings to all the globals and just leave the bindings for libraries.
Having a complete builtin set of web api bindings was the reason why bs-webapi was started as an incubator project. Unfortunately the experiment has shown that having general bindings for the whole webapi is too ambitious and probably not worth generalizing. That’s why we encourage writing your own specific bindings for more complex use cases in the first place.
Currently the Js module provides abstract types other libraries can rely on, so we can at least allow interoperability between libraries. It also comes with a few global functions that were easy to implement, such as setTimeout.
Anyways, this is more a question about Stdlib functionality and kinda unrelated to the feature proposed above, I think.
Ah yes! I keep forgetting because it’s hard for me to remember which methods are included in Global since it’s just a small set of all available in the real global.
regarding the decorators, personally I think that the names are a bit too generic (or abstract?). For example:
@val seems to me like it declares a value in a variable. I would rename it as @global @send I would like it sends a message or some XHR. Maybe I would alias it as @method or @callor @apply or @run
@new this one does seem straight forward @module also self-explanatory @set makes sense @get also ok for me
@apply and @call are very ambiguous too in the context of JS. @run just doesn’t tell you much, which leaves us with @method. But is the change worth it?
I didn’t know this! But anyways you are not always sending data when you use @send. It’s more like to bind a method. So I would say that @method is more intuitive for my persona as a web dev with JS as being my first language and no CS degree.