Closed bobzhang closed 1 week ago
Fable does something similar with its Emit
attribute, but uses templates with placeholders that mimic the actual JavaScript that will be generated instead of cryptic symbols that don't mean anything to anyone. See https://fable.io/docs/interacting.html#emit-attribute
Some examples of what this could look like:
external%bs sum : int -> int -> int = "sum($0, $1)"
let x = sum 1 2 (* Generates: var x = sum(1, 2) *)
(* Instead of [@bs.as {json|...|json}] *)
external%bs cloneNodeDeep : node -> node = "$0.cloneNode(true)"
external%bs attachShadowOpen : node -> node = "$0.attachShadow({ mode: 'open' })"
(* Generalized [@@bs.scope] *)
external%bs locationHref : document -> string = "$0.location.href"
(* Instead of [@@bs.set] *)
external%bs setTitle : document -> string -> unit = "$0.title = $1"
(* Instead of [@@bs.splice] / [@@bs.variadic] *)
external%bs add : collection -> string array -> unit = "$0.add($1...)"
(* Instead of [@@bs.new] *)
external%bs makeRegex : string -> regex = "new RegExp($0)"
Indeed, this looks more uniform, but it makes code analyzer more difficult. From what I can tell, it does not do any analysis or check though.
open Fable.Core
[<Emit("$0 - haha($1 + $2)")>]
let add (x: int) (y: string): float = jsNative
let result = add 1 "2"
generated code
export const result = 1 - haha("2" + null);
We could do some analysis, we will see how much effort it needs
It does check both the JavaScript and template syntax (there's a bit more to it than just placeholders), but does not check the placeholders against the type signature it seems.
Fable is built on Babel which as I understand includes a JS parser, so that part is probably trivial for them to implement. I don't think it's necessary to support the entire JS syntax either, even just to cover the existing FFI surface. The above examples are pretty simple syntactically, especially if arguments are restricted to JSON, which of course you already have a parser for.
Hygienic macros with full support for the entirety of JavaScript's syntax would be pretty neat though.
Idris 1 does something similar too, it's really convenient
@bobzhang is this feature being considered in the current "official" roadmap? Or rather something that contributors could help with? This addition would make BuckleScript way more accessible and expressive for JavaScript developers. ❤️
A more specific question, there seems to be a Flow parser vendored already. (js_of_ocaml also has its own parser as well, based on Menhir).
Is the idea to use the vendored Flow parser for the JS snippets in these expressions?
yeah, it is the road map(after we finish the data representation changes). The flow parser is more tested (used more).
On Wed, May 6, 2020 at 5:21 AM Javier Chávarri notifications@github.com wrote:
@bobzhang https://github.com/bobzhang is this feature being considered in the current "official" roadmap? Or rather something that contributors could help with? This addition would make BuckleScript way more accessible and expressive for JavaScript developers. ❤️
A more specific question, there seems to be a Flow parser vendored already https://github.com/bucklescript/bucklescript/blob/7015ce12d629fd9b6385045e5b486f54a941d956/jscomp/js_parser/flow_ast.ml#L9-L12. (js_of_ocaml also has its own parser https://github.com/ocsigen/js_of_ocaml/blob/8263a4cb60bcb6944a71e73626b152fb4f3d1622/compiler/lib/js_parser.mly as well, based on Menhir).
Is the idea to use the vendored Flow parser for the JS snippets in these expressions?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/BuckleScript/bucklescript/issues/3618#issuecomment-624312911, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFWMK7P6K6SCNTS5API34TRQB7MNANCNFSM4HZRJTHQ .
-- Regards -- Hongbo Zhang
I am thinking we can just use the most intuitive way of writing FFIs
external add : int -> int -> int = "function(x,y){
return x + y
}
Whether inline external or not is up to the compiler, for externals which are hard to inline, we can materialize it:
open struct
external add : ..
end
: sig
val add : int -> int -> int
end
Compared with let x = %raw
, we get polymorphism for free and can decide whether to inline it or not.
Extra work to do: detect import from third party packages
external add : int -> int -> int = {|
var {fancyAdd} = require('third-party')
function(x,y){
return fancyAdd(x, y)
}
|}
My 2c,
I kind of like the existing Bucklescript FFI over this:
external add : int -> int -> int = {|
var {fancyAdd} = require('third-party')
function(x,y){
return fancyAdd(x, y)
}
|}
Purescript does something like this, and the FFI turned out to be a much bigger pain point than Bucklescript because of this kind of thing. When I switched to Bucklescript, writing bindings became much less of a burden. It takes longer to write bindings and it's easier to screw something up in the inlined js code, which ends up eating up time debugging things.
I originally thought the proposal here would be more Idris-like, sort of like this:
(* Keep bs.module so that bucklescript can output multiple module systems like commonjs
* and es6 without it being hardcoded into the app code *)
external add : int -> int -> int = "fancyAdd($0, $1)" [@@bs.module "third-party"]
external add : int -> int -> int = "$0 + $1"
And from @glennsl 's comments, something like:
(* Generalized [@@bs.scope] *)
external locationHref : document -> string = "$0.location.href"
(* Instead of [@@bs.set] *)
external setTitle : document -> string -> unit = "$0.title = $1"
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
basic
similar projects: https://github.com/fdopen/ppx_cstubs