Skip to content

Commit 06743cb

Browse files
committed
Merge pull request #400 from bloomberg/ffi_doc
[docs] [skip ci] add docs for FFI
2 parents f279079 + 3ab9a1c commit 06743cb

File tree

2 files changed

+192
-73
lines changed

2 files changed

+192
-73
lines changed

docs/Extensions-to-OCaml-Language.md

Lines changed: 0 additions & 68 deletions
This file was deleted.

docs/OCaml-call-JS.md

Lines changed: 192 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,143 @@
1+
To make OCaml work smoothly with Javascript, we introduced several
2+
extensions to OCaml language. Those BuckleScript extensions
3+
facilitates the integration of native JavaScript code as well as
4+
improve the generated code.
5+
6+
> Note that all those extension will be correctly ignored by the native OCaml compiler.
7+
8+
9+
## Embedding raw Javascript code
10+
11+
- extension `bs.raw`
12+
13+
It can be either `[%bs.raw{| this_is_arbitrary_js_expression |}]` or `[%%bs.raw{| this is arbitrary_js_statement |}`
14+
15+
Use cases:
16+
for example if you want to use a JavaScript string, you can write code like this
17+
18+
```OCaml
19+
let x : string = [%bs.raw{|"\x01\x02"|}]
20+
```
21+
22+
which will be compiled into
23+
24+
```js
25+
var x = "\x01\x02"
26+
```
27+
28+
```OCaml
29+
[%%bs.raw{|
30+
// Math.imul polyfill
31+
if (!Math.imul){
32+
Math.imul = function (..) {..}
33+
}
34+
|}]
35+
```
36+
In the expression level, i.e, `[%bs.raw ...]` user can add a type annotation, the compiler would use such type annotation to deduce its arities. for example, the next three versions:
37+
38+
```ocaml
39+
let f = [%bs.raw ("Math.max" : float -> float -> float) ] 3.0
40+
let f : float -> float -> float = [%bs.raw "Math.max" ] 3.0
41+
let f = ([%bs.raw "Math.max"] : float -> float -> float ) 3.0
42+
```
43+
will be translated into
44+
45+
```js
46+
function f(prim){
47+
return Math.max(3.0,prim);
48+
}
49+
```
50+
Caveat:
51+
1. So far we don't do any sanity check in the quoted text (syntax check is a long-term goal)
52+
2. You should not refer symbols in OCaml code, it is not guaranteed that the order is correct.
53+
You should avoid introducing new symbols in the raw code, if needed, use the `$$` prefix (ie `$$your_func_name`)
54+
55+
## Debugger support
56+
57+
- extension `bs.debugger`
58+
59+
It can be `[%bs.debugger]`
60+
61+
use case
62+
63+
```ocaml
64+
let f x y =
65+
[%bs.debugger];
66+
x + y
67+
```
68+
69+
which will be compiled into
70+
71+
```js
72+
function f (x,y) {
73+
debugger; // JavaScript developer tools will set an breakpoint and stop here
74+
x + y;
75+
}
76+
```
77+
78+
## Native uncurried calling convention support
79+
80+
Note that OCaml's calling convention is curried by default, while JS
81+
does not have native support. Curried and uncurried functions can be
82+
told from type signatures.
83+
84+
For example
85+
86+
```ocaml
87+
val f : int -> string -> int
88+
val f_uncurry : int * string -> int [@uncurry]
89+
```
90+
91+
92+
93+
- How BuckleScript compiles function application
94+
95+
To apply a function, you can do this
96+
97+
```ocaml
98+
f 3 "x"
99+
f_uncurry #@ (3,"x")
100+
```
101+
For uncurried function applicaton, BuckleScript is guaranteed to
102+
compile it in the same way as JS code
103+
104+
```js
105+
f_uncurry(3,"x")
106+
```
107+
108+
However, for curried function application, it depends on how compiler
109+
optimizations goes, for most cases, when the compiler see the
110+
definition of `f`, it will compile it in the most efficient way, i.e,
111+
JS full aplication, if the compiler can not see the definition of `f`,
112+
it will do a runtime dispath, so there are two possible outputs:
113+
114+
```js
115+
Curry._2(f, 3, "x") // compiler fails to optimize
116+
f(3, "x") // compiler optimized correctly
117+
```
118+
Both are correct code, but the second one is more efficient.
119+
120+
- How BuckleScript handles function definition
121+
122+
```ocaml
123+
let f = fun a b -> a + string_of_int b
124+
let f_uncurry = fun %uncurry a b -> a + string_of_int b
125+
```
126+
127+
- When is uncurried function recommended
128+
129+
1. For FFI to JS functions, all object methods are *strongly recommended*
130+
to type it as uncurried function
131+
132+
133+
134+
2. When function is passed as a callback
135+
136+
This is mostly for performance issues, it is hard to optimize in
137+
such scenario
138+
139+
140+
1141

2142
## FFI to js functions
3143

@@ -151,8 +291,8 @@ gives it a type and customized attributes
151291
below is an example
152292

153293
```OCaml
154-
external describe : string -> (unit -> unit) -> unit = "describe" [@@bs.call]
155-
external it : string -> (unit -> unit) -> unit = "it" [@@bs.call "it"]
294+
external describe : string -> (unit -> unit [@uncurry]) -> unit = "describe" [@@bs.call]
295+
external it : string -> (unit -> unit [@uncurry]) -> unit = "it" [@@bs.call "it"]
156296
```
157297

158298
Since, `mochajs` is a test framework, we also need some assertion
@@ -168,7 +308,7 @@ On top of this we can write normal OCaml functions, for example:
168308
```OCaml
169309
let assert_equal = eq
170310
let from_suites name suite =
171-
describe name (fun _ ->
311+
describe name (fun%uncurry () ->
172312
List.iter (fun (name, code) -> it name code) suite)
173313
```
174314

@@ -193,6 +333,53 @@ On top of this we can write normal OCaml functions, for example:
193333

194334

195335

196-
## FFI to objects *experimental*
336+
## FFI to object
337+
338+
339+
- Js object calling
340+
All JS object of type `a` are lifted to type `a Js.t` to avoid
341+
conflict with OCaml's own object system. `##` is used in JS's object
342+
method dispatch, while `#` is used in OCaml's object method dispatch.
343+
344+
345+
For example
346+
347+
```ocaml
348+
let f x a b = x ## hi (a,b)
349+
```
350+
351+
is inferred as type
352+
353+
```ocaml
354+
val f : < hi : ('a * 'b -> 'c [@uncurry] ; .. > Js.t -> 'a -> 'b -> 'c
355+
```
356+
357+
- Create JS object
358+
359+
```ocaml
360+
let a = f ({ hi = fun %uncurry (x,y) -> x + y}[@bs.obj]) 1 2
361+
let b = f ({ hi = fun %uncurry (x,y) -> x +. y}[@bs.obj]) 1. 2.
362+
```
363+
364+
Generated code is like below
365+
366+
367+
```js
368+
function f(x, a, b) {
369+
return x.hi(a, b);
370+
}
371+
372+
var a = f({
373+
"hi": function (x, y) {
374+
return x + y | 0;
375+
}
376+
}, 1, 2);
377+
378+
var b = f({
379+
"hi": function (x, y) {
380+
return x + y;
381+
}
382+
}, 1, 2);
383+
```
384+
197385

198-
Note we will support using OCaml style's objects for FFI in the next release

0 commit comments

Comments
 (0)