getSet { light }
getSet({light: light})
payload that can be passed
+to `deriving`:
+
+```ocaml
+type person = {
+ name : string;
+ age : int;
+}
+[@@deriving jsProperties, getSet { light }]
+
+let alice = person ~name:"Alice" ~age:20
+let aliceName = name alice
+```
+```reasonml
+[@deriving (jsProperties, getSet({light: light}))]
+type person = {
+ name: string,
+ age: int,
+};
+
+let alice = person(~name="Alice", ~age=20);
+let aliceName = name(alice);
+```
+
+Which generates:
+
+```javascript
+var alice = {
+ name: "Alice",
+ age: 20
+};
+
+var aliceName = alice.name;
+```
+
+In this example, the getter functions share the same names as the object fields.
+Another distinction from the previous example is that the `person` constructor
+function no longer requires the final `unit` argument since we have excluded the
+optional field in this case.
+
+> **_NOTE:_** The `mel.as` attribute can still be applied to record fields when
+> the record type is annotated with `deriving`, allowing for the renaming of
+> fields in the resulting JavaScript objects, as demonstrated in the section
+> about [binding to objects with static
+> shape](./working-with-js-objects-and-values.md#objects-with-static-shape-record-like).
+> However, the option to pass indices to the `mel.as` decorator (like `[@mel.as
+> "0"]`) to change the runtime representation to an array is not available when
+> using `deriving`.
+
+##### Compatibility with OCaml features
+
+The `@deriving getSet` attribute and its lightweight variant can be used with
+[mutable
+fields](https://v2.ocaml.org/manual/coreexamples.html#s:imperative-features) and
+[private types](https://v2.ocaml.org/manual/privatetypes.html), which are
+features inherited by Melange from OCaml.
+
+When the record type has mutable fields, Melange will generate setter functions
+for them. For example:
+
+```ocaml
+type person = {
+ name : string;
+ mutable age : int;
+}
+[@@deriving getSet]
+
+let alice = person ~name:"Alice" ~age:20
+
+let () = ageSet alice 21
+```
+```reasonml
+[@deriving getSet]
+type person = {
+ name: string,
+ mutable age: int,
+};
+
+let alice = person(~name="Alice", ~age=20);
+
+let () = ageSet(alice, 21);
+```
+
+This will generate:
+
+```javascript
+var alice = {
+ name: "Alice",
+ age: 20
+};
+
+alice.age = 21;
+```
+
+If the `mutable` keyword is omitted from the interface file, Melange will not
+include the setter function in the module signature, preventing other modules
+from mutating any values from the type.
+
+Private types can be used to prevent Melange from creating the constructor
+function. For example, if we define `person` type as private:
+
+```ocaml
+type person = private {
+ name : string;
+ age : int;
+}
+[@@deriving getSet]
+```
+```reasonml
+[@deriving getSet]
+type person =
+ pri {
+ name: string,
+ age: int,
+ };
+```
+
+The accessors `nameGet` and `ageGet` will still be generated, but not the
+constructor `person`. This is useful when binding to JavaScript objects while
+preventing any Melange code from creating values of such type.
+
+## Use Melange code from JavaScript
+
+As mentioned in the [build system
+section](./build-system.md#commonjs-or-es6-modules), Melange allows to produce
+both CommonJS and ES6 modules. In both cases, using Melange-generated JavaScript
+code from any hand-written JavaScript file works as expected.
+
+The following definition:
+
+```ocaml
+let print name = "Hello" ^ name
+```
+```reasonml
+let print = name => "Hello" ++ name;
+```
+
+Will generate this JavaScript code, when using CommonJS (the default):
+
+```js
+function print(name) {
+ return "Hello" + name;
+}
+
+exports.print = print;
+```
+
+When using ES6 (through the `(module_systems es6)` field in `melange.emit`) this
+code will be generated:
+
+```js
+function print(name) {
+ return "Hello" + name;
+}
+
+export {
+ print ,
+}
+```
+
+So one can use either `require` or `import` (depending on the module system of
+choice) to import the `print` value in a JavaScript file.
+
+### Default ES6 values
+
+One special case occur when working with JavaScript imports in ES6 modules that
+look like this:
+
+```js
+import ten from 'numbers.js';
+```
+
+This import expects `numbers.js` to have a default export, like:
+
+```js
+export default ten = 10;
+```
+
+To emulate this kind of exports from Melange, one just needs to define a
+`default` value.
+
+For example, in a file named numbers.ml
numbers.re
:
+
+```ocaml
+let default = 10
+```
+```reasonml
+let default = 10;
+```
+
+That way, Melange will set the value on the `default` export so it can be
+consumed as default import on the JavaScript side.
diff --git a/docs/attributes-and-extension-nodes.md b/docs/attributes-and-extension-nodes.md
new file mode 100644
index 000000000..a4e73955b
--- /dev/null
+++ b/docs/attributes-and-extension-nodes.md
@@ -0,0 +1,305 @@
+# Melange attributes and extension nodes
+
+## Attributes
+
+These attributes are used to annotate `external` definitions:
+
+- [`mel.get`](./working-with-js-objects-and-values.md#bind-to-object-properties):
+ read JavaScript object properties statically by name, using the dot notation
+ `.`
+- [`mel.get_index`](./working-with-js-objects-and-values.md#bind-to-object-properties):
+ read a JavaScript object’s properties dynamically by using the bracket
+ notation `[]`
+- [`mel.module`](./working-with-js-objects-and-values.md#using-functions-from-other-javascript-modules):
+ bind to a value from a JavaScript module
+- [`mel.new`](./working-with-js-objects-and-values.md#javascript-classes): bind
+ to a JavaScript class constructor
+- [`mel.obj`](./working-with-js-objects-and-values.md#using-external-functions):
+ create a JavaScript object
+- [`mel.return`](./working-with-js-objects-and-values.md#wrapping-returned-nullable-values):
+ automate conversion from nullable values to `Option.t` values
+- [`mel.send`](./working-with-js-objects-and-values.md#calling-an-object-method):
+ call a JavaScript object method using [pipe
+ first](./language-concepts.md#pipe-first) convention
+- [`mel.send.pipe`](./working-with-js-objects-and-values.md#calling-an-object-method):
+ call a JavaScript object method using [pipe
+ last](./language-concepts.md#pipe-last) convention
+- [`mel.set`](./working-with-js-objects-and-values.md#bind-to-object-properties):
+ set JavaScript object properties statically by name, using the dot notation
+ `.`
+- [`mel.set_index`](./working-with-js-objects-and-values.md#bind-to-object-properties):
+ set JavaScript object properties dynamically by using the bracket notation
+ `[]`
+- [`mel.scope`](./working-with-js-objects-and-values.md#binding-to-properties-inside-a-module-or-global):
+ reach to deeper properties inside a JavaScript object
+- [`mel.splice`](./working-with-js-objects-and-values.md#variadic-function-arguments):
+ a deprecated attribute, is an alternate form of `mel.variadic`
+- [`mel.variadic`](./working-with-js-objects-and-values.md#variadic-function-arguments):
+ bind to a function taking variadic arguments from an array
+
+These attributes are used to annotate arguments in `external` definitions:
+
+- [`u`](./working-with-js-objects-and-values.md#binding-to-callbacks): define
+ function arguments as uncurried (manual)
+- [`mel.int`](./working-with-js-objects-and-values.md#using-polymorphic-variants-to-bind-to-enums):
+ compile function argument to an int
+- [`mel.string`](./working-with-js-objects-and-values.md#using-polymorphic-variants-to-bind-to-enums):
+ compile function argument to a string
+- [`mel.this`](./working-with-js-objects-and-values.md#modeling-this-based-callbacks):
+ bind to `this` based callbacks
+- [`mel.uncurry`](./working-with-js-objects-and-values.md#binding-to-callbacks):
+ define function arguments as uncurried (automated)
+- [`mel.unwrap`](./working-with-js-objects-and-values.md#approach-2-polymorphic-variant-mel-unwrap):
+ unwrap variant values
+
+These attributes are used in places like records, fields, arguments, functions,
+and more:
+
+- `mel.as`: redefine the name generated in the JavaScript output code. Used in
+ [constant function
+ arguments](./working-with-js-objects-and-values.md#constant-values-as-arguments),
+ [variants](./advanced-js-interop.md#conversion-functions), polymorphic
+ variants (either [inlined in external
+ functions](./working-with-js-objects-and-values.md#using-polymorphic-variants-to-bind-to-enums)
+ or [in type definitions](./advanced-js-interop.md#polymorphic-variants)) and
+ [record
+ fields](./working-with-js-objects-and-values.md#objects-with-static-shape-record-like).
+- [`deriving`](./advanced-js-interop.md#generate-getters-setters-and-constructors):
+ generate getters and setters for some types
+- [`mel.inline`](#inlining-constant-values): forcefully inline constant values
+- [`optional`](./advanced-js-interop.md#generate-javascript-objects-with-optional-properties):
+ translates optional fields in a record to omitted properties in the generated
+ JavaScript object (combines with `deriving`)
+
+## Extension nodes
+
+In order to use any of these extension nodes, you will have to add the melange
+PPX preprocessor to your project. To do so, add the following to the `dune`
+file:
+
+```dune
+(library
+ (name lib)
+ (modes melange)
+ (preprocess
+ (pps melange.ppx)))
+```
+
+The same field `preprocess` can be added to `melange.emit`.
+
+Here is the list of all the extension nodes supported by Melange:
+
+- [`mel.debugger`](#debugger): insert `debugger` statements
+- [`mel.external`](#detect-global-variables): read global values
+- [`mel.obj`](./working-with-js-objects-and-values.md#using-js-t-objects):
+ create JavaScript object literals
+- [`mel.raw`](#generate-raw-javascript): write raw JavaScript code
+- [`mel.re`](./data-types-and-runtime-rep.md#regular-expressions): insert
+ regular expressions
+
+## Generate raw JavaScript
+
+It is possible to directly write JavaScript code from a Melange file. This is
+unsafe, but it can be useful for prototyping or as an escape hatch.
+
+To do it, we will use the `mel.raw`
+[extension](https://v2.ocaml.org/manual/extensionnodes.html):
+
+```ocaml
+let add = [%mel.raw {|
+ function(a, b) {
+ console.log("hello from raw JavaScript!");
+ return a + b;
+ }
+|}]
+
+let () = Js.log (add 1 2)
+```
+```reasonml
+let add = [%mel.raw
+ {|
+ function(a, b) {
+ console.log("hello from raw JavaScript!");
+ return a + b;
+ }
+|}
+];
+
+let () = Js.log(add(1, 2));
+```
+
+The `{||}` strings are called ["quoted
+strings"](https://ocaml.org/manual/lex.html#quoted-string-id). They are similar
+to JavaScript’s template literals, in the sense that they are multi-line, and
+there is no need to escape characters inside the string.
+
+Using one percentage signthe extension name between square brackets
+(`[%mel.raw 'a Option.t
Option.t('a)
, meaning its wrapped value is
+compatible with any type. If you use the value, it is recommended to annotate it
+into a known type first to avoid runtime issues.
+
+## Inlining constant values
+
+Some JavaScript idioms require special constants to be inlined since they serve
+as de-facto directives for bundlers. A common example is `process.env.NODE_ENV`:
+
+```js
+if (process.env.NODE_ENV !== "production") {
+ // Development-only code
+}
+```
+
+becomes:
+
+```js
+if ("development" !== "production") {
+ // Development-only code
+}
+```
+
+In this case, bundlers such as Webpack can tell that the `if` statement always
+evaluates to a specific branch and eliminate the others.
+
+Melange provides the `mel.inline` attribute to achieve the same goal in
+generated JavaScript. Let’s look at an example:
+
+```ocaml
+external node_env : string = "NODE_ENV" [@@mel.scope "process", "env"]
+
+let development = "development"
+let () = if node_env <> development then Js.log "Only in Production"
+
+let development_inline = "development" [@@mel.inline]
+let () = if node_env <> development_inline then Js.log "Only in Production"
+```
+```reasonml
+[@mel.scope ("process", "env")] external node_env: string = "NODE_ENV";
+
+let development = "development";
+let () =
+ if (node_env != development) {
+ Js.log("Only in Production");
+ };
+
+[@mel.inline]
+let development_inline = "development";
+let () =
+ if (node_env != development_inline) {
+ Js.log("Only in Production");
+ };
+```
+
+As we can see in the generated JavaScript presented below:
+
+- the `development` variable is emitted
+ - it gets used as a variable `process.env.NODE_ENV !== development` in the
+ `if` statement
+- the `development_inline` variable isn’t present in the final output
+ - its value is inlined in the `if` statement: `process.env.NODE_ENV !==
+ "development"`
+
+```js
+var development = "development";
+
+if (process.env.NODE_ENV !== development) {
+ console.log("Only in Production");
+}
+
+if (process.env.NODE_ENV !== "development") {
+ console.log("Only in Production");
+}
+```
diff --git a/docs/bindings-cookbook.md b/docs/bindings-cookbook.md
new file mode 100644
index 000000000..0a1ac4798
--- /dev/null
+++ b/docs/bindings-cookbook.md
@@ -0,0 +1,633 @@
+# Bindings cookbook
+
+## Globals
+
+### `window`: global variable
+
+```ocaml
+external window : Dom.window = "window"
+```
+```reasonml
+external window: Dom.window = "window";
+```
+
+See the [Using global functions or
+values](./working-with-js-objects-and-values.md#using-global-functions-or-values)
+section for more information.
+
+### `window?`: does global variable exist
+
+```ocaml
+let _ = match [%mel.external window] with
+| Some _ -> "window exists"
+| None -> "window does not exist"
+```
+```reasonml
+let _ =
+ switch ([%mel.external window]) {
+ | Some(_) => "window exists"
+ | None => "window does not exist"
+ };
+```
+
+See the [Detect global
+variables](./attributes-and-extension-nodes.md#detect-global-variables) section
+for more information.
+
+### `Math.PI`: variable in global module
+
+```ocaml
+external pi : float = "PI" [@@mel.scope "Math"]
+```
+```reasonml
+[@mel.scope "Math"] external pi: float = "PI";
+```
+
+See the [Binding to properties inside a module or
+global](./working-with-js-objects-and-values.md#binding-to-properties-inside-a-module-or-global)
+section for more information.
+
+### `console.log`: function in global module
+
+```ocaml
+external log : 'a -> unit = "log" [@@mel.scope "console"]
+```
+```reasonml
+[@mel.scope "console"] external log: 'a => unit = "log";
+```
+
+See the [Binding to properties inside a module or
+global](./working-with-js-objects-and-values.md#binding-to-properties-inside-a-module-or-global)
+section for more information.
+
+## Modules
+
+### `const path = require('path'); path.join('a', 'b')`: function in CommonJS/ES6 module
+
+```ocaml
+external join : string -> string -> string = "join" [@@mel.module "path"]
+let dir = join "a" "b"
+```
+```reasonml
+[@mel.module "path"] external join: (string, string) => string = "join";
+let dir = join("a", "b");
+```
+
+See the [Using functions from other JavaScript
+modules](./working-with-js-objects-and-values.md#using-functions-from-other-javascript-modules)
+section for more information.
+
+### `const foo = require('foo'); foo(1)`: import entire module as a value
+
+```ocaml
+external foo : int -> unit = "foo" [@@mel.module]
+let () = foo 1
+```
+```reasonml
+[@mel.module] external foo: int => unit = "foo";
+let () = foo(1);
+```
+
+See the [Using functions from other JavaScript
+modules](./working-with-js-objects-and-values.md#using-functions-from-other-javascript-modules)
+section for more information.
+
+### `import foo from 'foo'; foo(1)`: import ES6 module default export
+
+```ocaml
+external foo : int -> unit = "default" [@@mel.module "foo"]
+let () = foo 1
+```
+```reasonml
+[@mel.module "foo"] external foo: int => unit = "default";
+let () = foo(1);
+```
+
+See the [Using functions from other JavaScript
+modules](./working-with-js-objects-and-values.md#using-functions-from-other-javascript-modules)
+section for more information.
+
+### `const foo = require('foo'); foo.bar.baz()`: function scoped inside an object in a module
+
+```ocaml
+module Foo = struct
+ module Bar = struct
+ external baz : unit -> unit = "baz" [@@mel.module "foo"] [@@mel.scope "bar"]
+ end
+end
+
+let () = Foo.Bar.baz ()
+```
+```reasonml
+module Foo = {
+ module Bar = {
+ [@mel.module "foo"] [@mel.scope "bar"] external baz: unit => unit = "baz";
+ };
+};
+
+let () = Foo.Bar.baz();
+```
+
+It is not necessary to nest the binding inside OCaml modules, but mirroring the
+structure of the JavaScript module layout makes the binding more discoverable.
+
+See the [Binding to properties inside a module or
+global](./working-with-js-objects-and-values.md#binding-to-properties-inside-a-module-or-global)
+section for more information.
+
+## Functions
+
+### `const dir = path.join('a', 'b', ...)`: function with rest args
+
+```ocaml
+external join : string array -> string = "join" [@@mel.module "path"] [@@mel.variadic]
+let dir = join [| "a"; "b" |]
+```
+```reasonml
+[@mel.module "path"] [@mel.variadic]
+external join: array(string) => string = "join";
+let dir = join([|"a", "b"|]);
+```
+
+See the [Variadic function
+arguments](./working-with-js-objects-and-values.md#variadic-function-arguments)
+section for more information.
+
+### `const nums = range(start, stop, step)`: call a function with named arguments for readability
+
+```ocaml
+external range : start:int -> stop:int -> step:int -> int array = "range"
+let nums = range ~start:1 ~stop:10 ~step:2
+```
+```reasonml
+external range: (~start: int, ~stop: int, ~step: int) => array(int) = "range";
+let nums = range(~start=1, ~stop=10, ~step=2);
+```
+
+### `foo('hello'); foo(true)`: overloaded function
+
+```ocaml
+external fooString : string -> unit = "foo"
+external fooBool : bool -> unit = "foo"
+
+let () = fooString ""
+let () = fooBool true
+```
+```reasonml
+external fooString: string => unit = "foo";
+external fooBool: bool => unit = "foo";
+
+let () = fooString("");
+let () = fooBool(true);
+```
+
+Melange allows specifying the name on the OCaml side and the name on the
+JavaScript side (in quotes) separately, so it's possible to bind multiple times
+to the same function with different names and signatures. This allows binding to
+complex JavaScript functions with polymorphic behaviour.
+
+### `const nums = range(start, stop, [step])`: optional final argument(s)
+
+```ocaml
+external range : start:int -> stop:int -> ?step:int -> unit -> int array
+ = "range"
+
+let nums = range ~start:1 ~stop:10 ()
+```
+```reasonml
+external range: (~start: int, ~stop: int, ~step: int=?, unit) => array(int) =
+ "range";
+
+let nums = range(~start=1, ~stop=10, ());
+```
+
+When an OCaml function takes an optional parameter, it needs a positional
+parameter at the end of the parameter list to help the compiler understand when
+function application is finished and when the function can actually execute. This
+might seem cumbersome, but it is necessary in order to have out-of-the-box curried
+parameters, named parameters, and optional parameters available in the language.
+
+### `mkdir('src/main', {recursive: true})`: options object argument
+
+```ocaml
+type mkdirOptions
+
+external mkdirOptions : ?recursive:bool -> unit -> mkdirOptions = "" [@@mel.obj]
+external mkdir : string -> ?options:mkdirOptions -> unit -> unit = "mkdir"
+
+let () = mkdir "src" ()
+let () = mkdir "src/main" ~options:(mkdirOptions ~recursive:true ()) ()
+```
+```reasonml
+type mkdirOptions;
+
+[@mel.obj] external mkdirOptions: (~recursive: bool=?, unit) => mkdirOptions;
+external mkdir: (string, ~options: mkdirOptions=?, unit) => unit = "mkdir";
+
+let () = mkdir("src", ());
+let () = mkdir("src/main", ~options=mkdirOptions(~recursive=true, ()), ());
+```
+
+See the [Objects with static shape (record-like): Using external
+functions](./working-with-js-objects-and-values.md#using-external-functions)
+section for more information.
+
+### `forEach(start, stop, item => console.log(item))`: model a callback
+
+```ocaml
+external forEach :
+ start:int -> stop:int -> ((int -> unit)[@mel.uncurry]) -> unit = "forEach"
+
+let () = forEach ~start:1 ~stop:10 Js.log
+```
+```reasonml
+external forEach:
+ (~start: int, ~stop: int, [@mel.uncurry] (int => unit)) => unit =
+ "forEach";
+
+let () = forEach(~start=1, ~stop=10, Js.log);
+```
+
+When binding to functions with callbacks, you'll want to ensure that the
+callbacks are uncurried. `[@mel.uncurry]` is the recommended way of doing that.
+However, in some circumstances you may be forced to use the static uncurried
+function syntax. See the [Binding to
+callbacks](./working-with-js-objects-and-values.md#binding-to-callbacks) section
+for more information.
+
+## Objects
+
+### `const person = {id: 1, name: 'Alice'}`: create an object
+
+For quick creation of objects (e.g. prototyping), one can create a `Js.t` object
+literal directly:
+
+```ocaml
+let person = [%mel.obj { id = 1; name = "Alice" }]
+```
+```reasonml
+let person = {"id": 1, "name": "Alice"};
+```
+
+See the [Using `Js.t`
+objects](./working-with-js-objects-and-values.md#using-js-t-objects) section for
+more information.
+
+Alternatively, for greater type accuracy, one can create a record type and a
+value:
+
+```ocaml
+type person = { id : int; name : string }
+let person = { id = 1; name = "Alice" }
+```
+```reasonml
+type person = {
+ id: int,
+ name: string,
+};
+let person = {id: 1, name: "Alice"};
+```
+
+See the [Using OCaml
+records](./working-with-js-objects-and-values.md#using-ocaml-records) section
+for more information.
+
+### `person.name`: get a prop
+
+
+```ocaml
+let name = person##name
+```
+```reasonml
+let name = person##name;
+```
+
+Alternatively, if `person` value is of record type as mentioned in the section
+above:
+
+
+```ocaml
+let name = person.name
+```
+```reasonml
+let name = person.name;
+```
+
+### `person.id = 0`: set a prop
+
+
+```ocaml
+external set_id : person -> int -> unit = "id" [@@mel.set]
+
+let () = set_id person 0
+```
+```reasonml
+[@mel.set] external set_id: (person, int) => unit = "id";
+
+let () = set_id(person, 0);
+```
+
+### `const {id, name} = person`: object with destructuring
+
+```ocaml
+type person = { id : int; name : string }
+
+let person = { id = 1; name = "Alice" }
+let { id; name } = person
+```
+```reasonml
+type person = {
+ id: int,
+ name: string,
+};
+
+let person = {id: 1, name: "Alice"};
+let {id, name} = person;
+```
+
+## Classes and OOP
+
+In Melange it is idiomatic to bind to class properties and methods as functions
+which take the instance as just a normal function argument. So e.g., instead of
+
+```javascript
+const foo = new Foo();
+foo.bar();
+```
+
+You will write:
+
+
+```ocaml
+let foo = Foo.make ()
+let () = Foo.bar foo
+```
+```reasonml
+let foo = Foo.make();
+let () = Foo.bar(foo);
+```
+
+Note that many of the techniques shown in the [Functions](#functions) section
+are applicable to the instance members shown below.
+
+### `const foo = new Foo()`: call a class constructor
+
+```ocaml
+module Foo = struct
+ type t
+ external make : unit -> t = "Foo" [@@mel.new]
+end
+
+let foo = Foo.make ()
+```
+```reasonml
+module Foo = {
+ type t;
+ [@mel.new] external make: unit => t = "Foo";
+};
+
+let foo = Foo.make();
+```
+
+Note the abstract type `t`, which we have revisited already in [its
+corresponding](./language-concepts.md#abstract-types) section.
+
+A Melange function binding doesn't have the context that it's binding to a
+JavaScript class like `Foo`, so you will want to explicitly put it inside a
+corresponding module `Foo` to denote the class it belongs to. In other words,
+model JavaScript classes as OCaml modules.
+
+See the [JavaScript
+classes](./working-with-js-objects-and-values.md#javascript-classes) section for
+more information.
+
+### `const bar = foo.bar`: get an instance property
+
+```ocaml
+module Foo = struct
+ type t
+ external make : unit -> t = "Foo" [@@mel.new]
+ external bar : t -> int = "bar" [@@mel.get]
+end
+
+let foo = Foo.make ()
+let bar = Foo.bar foo
+```
+```reasonml
+module Foo = {
+ type t;
+ [@mel.new] external make: unit => t = "Foo";
+ [@mel.get] external bar: t => int = "bar";
+};
+
+let foo = Foo.make();
+let bar = Foo.bar(foo);
+```
+
+See the [Binding to object
+properties](./working-with-js-objects-and-values.md#bind-to-object-properties)
+section for more information.
+
+### `foo.bar = 1`: set an instance property
+
+```ocaml
+module Foo = struct
+ type t
+ external make : unit -> t = "Foo" [@@mel.new]
+ external setBar : t -> int -> unit = "bar" [@@mel.set]
+end
+
+let foo = Foo.make ()
+let () = Foo.setBar foo 1
+```
+```reasonml
+module Foo = {
+ type t;
+ [@mel.new] external make: unit => t = "Foo";
+ [@mel.set] external setBar: (t, int) => unit = "bar";
+};
+
+let foo = Foo.make();
+let () = Foo.setBar(foo, 1);
+```
+
+### `foo.meth()`: call a nullary instance method
+
+```ocaml
+module Foo = struct
+ type t
+
+ external make : unit -> t = "Foo" [@@mel.new]
+ external meth : t -> unit = "meth" [@@mel.send]
+end
+
+let foo = Foo.make ()
+let () = Foo.meth foo
+```
+```reasonml
+module Foo = {
+ type t;
+
+ [@mel.new] external make: unit => t = "Foo";
+ [@mel.send] external meth: t => unit = "meth";
+};
+
+let foo = Foo.make();
+let () = Foo.meth(foo);
+```
+
+See the [Calling an object
+method](./working-with-js-objects-and-values.md#calling-an-object-method)
+section for more information.
+
+### `const newStr = str.replace(substr, newSubstr)`: non-mutating instance method
+
+```ocaml
+external replace : substr:string -> newSubstr:string -> string = "replace"
+[@@mel.send.pipe: string]
+
+let str = "goodbye world"
+let substr = "goodbye"
+let newSubstr = "hello"
+let newStr = replace ~substr ~newSubstr str
+```
+```reasonml
+[@mel.send.pipe: string]
+external replace: (~substr: string, ~newSubstr: string) => string = "replace";
+
+let str = "goodbye world";
+let substr = "goodbye";
+let newSubstr = "hello";
+let newStr = replace(~substr, ~newSubstr, str);
+```
+
+`mel.send.pipe` injects a parameter of the given type (in this case `string`) as
+the final positional parameter of the binding. In other words, it creates the
+binding with the real signature substr:string -\>
+newSubstr:string -\> string -\> string
(~substr: string, ~newSubstr: string, string) =\>
+string
. This is handy for non-mutating functions as they traditionally
+take the instance as the final parameter.
+
+It is not strictly necessary to use named arguments in this binding, but it
+helps readability with multiple arguments, especially if some have the same
+type.
+
+Also note that it is not strictly need to use `mel.send.pipe`, one can use
+`mel.send` everywhere.
+
+See the [Calling an object
+method](./working-with-js-objects-and-values.md#calling-an-object-method)
+section for more information.
+
+### `arr.sort(compareFunction)`: mutating instance method
+
+
+```ocaml
+external sort : 'a array -> (('a -> 'a -> int)[@mel.uncurry]) -> 'a array
+ = "sort"
+[@@mel.send]
+
+let _ = sort arr compare
+```
+```reasonml
+[@mel.send]
+external sort: (array('a), [@mel.uncurry] (('a, 'a) => int)) => array('a) =
+ "sort";
+
+let _ = sort(arr, compare);
+```
+
+For a mutating method, it's traditional to pass the instance argument first.
+
+Note: `compare` is a function provided by the standard library, which fits the
+defined interface of JavaScript's comparator function.
+
+## Null and undefined
+
+### `foo.bar === undefined`: check for undefined
+
+```ocaml
+module Foo = struct
+ type t
+ external bar : t -> int option = "bar" [@@mel.get]
+end
+
+external foo : Foo.t = "foo"
+
+let _result = match Foo.bar foo with Some _ -> 1 | None -> 0
+```
+```reasonml
+module Foo = {
+ type t;
+ [@mel.get] external bar: t => option(int) = "bar";
+};
+
+external foo: Foo.t = "foo";
+
+let _result =
+ switch (Foo.bar(foo)) {
+ | Some(_) => 1
+ | None => 0
+ };
+```
+
+If you know some value may be `undefined` (but not `null`, see next section),
+and if you know its type is monomorphic (i.e. not generic), then you can model
+it directly as an `Option.t` type.
+
+See the [Non-shared data
+types](./data-types-and-runtime-rep.md#non-shared-data-types) section for more
+information.
+
+### `foo.bar == null`: check for null or undefined
+
+```ocaml
+module Foo = struct
+ type t
+ external bar : t -> t option = "bar" [@@mel.get] [@@mel.return nullable]
+end
+
+external foo : Foo.t = "foo"
+
+let _result = match Foo.bar foo with Some _ -> 1 | None -> 0
+```
+```reasonml
+module Foo = {
+ type t;
+ [@mel.get] [@mel.return nullable] external bar: t => option(t) = "bar";
+};
+
+external foo: Foo.t = "foo";
+
+let _result =
+ switch (Foo.bar(foo)) {
+ | Some(_) => 1
+ | None => 0
+ };
+```
+
+If you know the value is 'nullable' (i.e. could be `null` or `undefined`), or if
+the value could be polymorphic, then `mel.return nullable` is appropriate to
+use.
+
+Note that this attribute requires the return type of the binding to be an
+`option` type as well.
+
+See the [Wrapping returned nullable
+values](./working-with-js-objects-and-values.md#wrapping-returned-nullable-values)
+section for more information.
diff --git a/docs/communicate-with-javascript.md b/docs/communicate-with-javascript.md
index 477bf2330..29d0c23b8 100644
--- a/docs/communicate-with-javascript.md
+++ b/docs/communicate-with-javascript.md
@@ -1,3874 +1,38 @@
# Communicate with JavaScript
Melange interoperates very well with JavaScript, and provides a wide array of
-features to communicate with foreign JavaScript code. To learn about these
-techniques (generically known as "bindings"), we will first go through the
-language concepts that they build upon, then we will see how types in Melange
-map to JavaScript runtime types. Finally, we will provide a variety of use cases
-with examples to show how to communicate to and from JavaScript.
-
-If you already have in mind some JavaScript that you want to write, check the
-last section ["Bindings cookbook"](#bindings-cookbook) to see how the same code
-can be written with Melange.
-
-## Language concepts
-
-The concepts covered in the following sections are a small subset of the OCaml
-language. However, they are essential for understanding how to communicate with
-JavaScript, and the features that Melange exposes to do so.
-
-### Attributes and extension nodes
-
-In order to interact with JavaScript, Melange needs to extend the language to
-provide blocks that express these interactions.
-
-One approach could be to introduce new syntactic constructs (keywords and such)
-to do so, for example:
-
-```text
-javascript add : int -> int -> int = {|function(x,y){
- return x + y
-}|}
-```
-
-But this would break compatibility with OCaml, which is one of the main goals of
-Melange.
-
-Fortunately, OCaml provides mechanisms to extend its language without breaking
-compatibility with the parser or the language. These mechanisms are composed by
-two parts:
-
-- First, some syntax additions to define parts of the code that will be extended
- or replaced
-- Second, compile-time OCaml native programs called [PPX
- rewriters](https://ocaml.org/docs/metaprogramming), that will read the syntax
- additions defined above and proceed to extend or replace them
-
-The syntax additions come in two flavors, called [extension
-nodes](https://v2.ocaml.org/manual/extensionnodes.html) and
-[attributes](https://v2.ocaml.org/manual/attributes.html).
-
-#### Extension nodes
-
-Extension nodes are blocks that are supposed to be replaced by a specific type
-of PPX rewriters called extenders. Extension nodes use the `%` character to be
-identified. Extenders will take the extension node and replace it with a valid
-OCaml AST (abstract syntax tree).
-
-An example where Melange uses extension nodes to communicate with JavaScript is
-to produce "raw" JavaScript inside a Melange program:
-
-```ocaml
-[%%mel.raw "var a = 1; var b = 2"]
-let add = [%mel.raw "a + b"]
-```
-```reasonml
-[%%mel.raw "var a = 1; var b = 2"];
-let add = [%mel.raw "a + b"];
-```
-
-Which will generate the following JavaScript code:
-
-```js
-var a = 1; var b = 2
-var add = a + b
-```
-
-The difference between one and two percentage characters is detailed in the
-[OCaml documentation](https://v2.ocaml.org/manual/extensionnodes.html).
-
-#### Attributes
-
-Attributes are "decorations" applied to specific parts of the code to provide
-additional information. In Melange, attributes are used in two ways to enhance
-the expressiveness of generating JavaScript code: either reusing existing OCaml
-built-in attributes or defining new ones.
-
-##### Reusing OCaml attributes
-
-The first approach is leveraging the existing [OCaml’s built-in
-attributes](https://v2.ocaml.org/manual/attributes.html#ss:builtin-attributes)
-to be used for JavaScript generation.
-
-One prominent example of OCaml attributes that can be used in Melange projects
-is the `unboxed` attribute, which optimizes the compilation of single-field
-records and variants with a single tag to their raw values. This is useful when
-defining type aliases that we don’t want to mix up, or when binding to
-JavaScript code that uses heterogeneous collections. An example of the latter is
-discussed in the [variadic function arguments](#variadic-function-arguments)
-section.
-
-For instance:
-
-```ocaml
-type name =
- | Name of string [@@unboxed]
-let student_name = Name "alice"
-```
-```reasonml
-[@unboxed]
-type name =
- | Name(string);
-let student_name = Name("alice");
-```
-
-Compiles into:
-
-```js
-var student_name = "alice";
-```
-
-Other OCaml pre-built attributes like `alert` or `deprecated` can be used with
-Melange as well.
-
-##### Defining new attributes
-
-The second approach is introducing new attributes specifically designed for
-Melange, such as the [`mel.set` attribute](#bind-to-object-properties) used to
-bind to properties of JavaScript objects. The complete list of attributes
-introduced by Melange can be found
-[here](#list-of-attributes-and-extension-nodes).
-
-Attribute annotations can use one, two or three `@` characters depending on
-their placement in the code and which kind of syntax tree node they are
-annotating. More information about attributes can be found in the [dedicated
-OCaml manual page](https://v2.ocaml.org/manual/attributes.html).
-
-Here are some samples using Melange attributes
-[`mel.set`](#bind-to-object-properties) and [`mel.as`](#using-ocaml-records):
-
-```ocaml
-type document
-external setTitleDom : document -> string -> unit = "title" [@@mel.set]
-
-type t = {
- age : int; [@mel.as "a"]
- name : string; [@mel.as "n"]
-}
-```
-```reasonml
-type document;
-[@mel.set] external setTitleDom: (document, string) => unit = "title";
-
-type t = {
- [@mel.as "a"]
- age: int,
- [@mel.as "n"]
- name: string,
-};
-```
-
-To learn more about preprocessors, attributes and extension nodes, check the
-[section about PPX
-rewriters](https://ocaml.org/docs/metaprogramming#ppx-rewriters) in the OCaml
-docs.
-
-### External functions
-
-Most of the system that Melange exposes to communicate with JavaScript is built
-on top of an OCaml language construct called `external`.
-
-`external` is a keyword for declaring a value in OCaml that will [interface with
-C code](https://v2.ocaml.org/manual/intfc.html):
-
-```ocaml
-external my_c_function : int -> string = "someCFunctionName"
-```
-```reasonml
-external my_c_function: int => string = "someCFunctionName";
-```
-
-It is like a `let` binding, except that the body of an external is a string.
-That string has a specific meaning depending on the context. For native OCaml,
-it usually refers to a C function with that name. For Melange, it refers to the
-functions or values that exist in the runtime JavaScript code, and will be used
-from Melange.
-
-In Melange, externals can be used to [bind to global JavaScript
-objects](#using-global-functions-or-values). They can also be decorated with
-certain `[@mel.xxx]` attributes to facilitate the creation of bindings in
-specific scenarios. Each one of the [available
-attributes](#list-of-attributes-and-extension-nodes) will be further explained
-in the next sections.
-
-Once declared, one can use an `external` as a normal value. Melange external
-functions are turned into the expected JavaScript values, inlined into their
-callers during compilation, and completely erased afterwards. They don’t appear
-in the JavaScript output, so there are no costs on bundling size.
-
-**Note**: it is recommended to use external functions and the `[@mel.xxx]`
-attributes in the interface files as well, as this allows some optimizations
-where the resulting JavaScript values can be directly inlined at the call sites.
-
-#### Special identity external
-
-One external worth mentioning is the following one:
-
-```ocaml
-type foo = string
-type bar = int
-external danger_zone : foo -> bar = "%identity"
-```
-```reasonml
-type foo = string;
-type bar = int;
-external danger_zone: foo => bar = "%identity";
-```
-
-This is a final escape hatch which does nothing but convert from the type `foo`
-to `bar`. In the following sections, if you ever fail to write an `external`,
-you can fall back to using this one. But try not to.
-
-### Abstract types
-
-In the subsequent sections, you will come across examples of bindings where a
-type is defined without being assigned to a value. Here is an example:
-
-```ocaml
-type document
-```
-```reasonml
-type document;
-```
-
-These types are referred to as "abstract types" and are commonly used together
-with external functions that define operations over values when communicating
-with JavaScript.
-
-Abstract types enable defining types for specific values originating from
-JavaScript while omitting unnecessary details. An illustration is the `document`
-type mentioned earlier, which has several
-[properties](https://developer.mozilla.org/en-US/docs/Web/API/Document). By
-using abstract types, one can focus solely on the required aspects of the
-`document` value that the Melange program requires, rather than defining all its
-properties. Consider the following example:
-
-```ocaml
-type document
-
-external document : document = "document"
-external set_title : document -> string -> unit = "title" [@@mel.set]
-```
-```reasonml
-type document;
-
-external document: document = "document";
-[@mel.set] external set_title: (document, string) => unit = "title";
-```
-
-Subsequent sections delve into the details about the
-[`mel.set`](#bind-to-object-properties) attribute and [how to bind to global
-values](#using-global-functions-or-values) like `document`.
-
-For a comprehensive understanding of abstract types and their usefulness, refer
-to the "Encapsulation" section of the [OCaml Cornell
-textbook](https://cs3110.github.io/textbook/chapters/modules/encapsulation.html).
-
-### Pipe operators
-
-There are two pipe operators available in Melange:
-
-- A _pipe last_ operator `|>`, available [in
- OCaml](https://v2.ocaml.org/api/Stdlib.html#1_Compositionoperators) and
- inherited in Melange
-- A _pipe first_ operator \|.
\-\>
, available exclusively in Melange
-
-Let’s see the differences between the two.
-
-#### Pipe last
-
-Since version 4.01, OCaml includes a reverse application or "pipe" (`|>`)
-operator, an infix operator that applies the result from the previous expression
-the next function. As a backend for OCaml, Melange inherits this operator.
-
-The pipe operator could be implemented like this (the real implementation is a
-bit
-[different](https://github.com/ocaml/ocaml/blob/d9547617e8b14119beacafaa2546cbebfac1bfe5/stdlib/stdlib.ml#L48)):
-
-```ocaml
-let ( |> ) f g = g f
-```
-```reasonml
-let (|>) = (f, g) => g(f);
-```
-
-This operator is useful when multiple functions are applied to some value in
-sequence, with the output of each function becoming the input of the next (a
-_pipeline_).
-
-For example, assuming we have a function `square` defined as:
-
-```ocaml
-let square x = x * x
-```
-```reasonml
-let square = x => x * x;
-```
-
-We are using it like:
-
-```ocaml
-let ten = succ (square 3)
-```
-```reasonml
-let ten = succ(square(3));
-```
-
-The pipe operator allows to write the computation for `ten` in left-to-right
-order, as [it has left
-associativity](https://v2.ocaml.org/manual/expr.html#ss:precedence-and-associativity):
-
-```ocaml
-let ten = 3 |> square |> succ
-```
-```reasonml
-let ten = 3 |> square |> succ;
-```
-
-When working with functions that can take multiple arguments, the pipe operator
-works best when the functions take the data we are processing as the last
-argument. For example:
-
-```ocaml
-let sum = List.fold_left ( + ) 0
-
-let sum_sq =
- [ 1; 2; 3 ]
- |> List.map square (* [1; 4; 9] *)
- |> sum (* 1 + 4 + 9 *)
-```
-```reasonml
-let sum = List.fold_left((+), 0);
-
-let sum_sq =
- [1, 2, 3]
- |> List.map(square) /* [1; 4; 9] */
- |> sum; /* 1 + 4 + 9 */
-```
-
-The above example can be written concisely because the `List.map` function in
-the [OCaml standard library](https://v2.ocaml.org/api/Stdlib.List.html) takes
-the list as the second argument. This convention is sometimes referred to as
-"data-last", and is widely adopted in the OCaml ecosystem. Data-last and the
-pipe operator `|>` work great with currying, so they are a great fit for the
-language.
-
-However, there are some limitations when using data-last when it comes to error
-handling. In the given example, if we mistakenly used the wrong function:
-
-```ocaml
-let sum_sq =
- [ 1; 2; 3 ]
- |> List.map String.cat
- |> sum
-```
-```reasonml
-let sum_sq = [1, 2, 3] |> List.map(String.cat) |> sum;
-```
-
-The compiler would rightfully raise an error:
-
-4 | [ 1; 2; 3 ]
- ^
- Error: This expression has type int but an expression was expected of type
- string
- 1 | [ 1, 2, 3 ]
- ^
- Error: This expression has type int but an expression was expected of type
- string
-\|.
\-\>
.
-
-#### Pipe first
-
-To overcome the constraints mentioned above, Melange introduces the pipe first
-operator \|.
\-\>
.
-
-As its name suggests, the pipe first operator is better suited for functions
-where the data is passed as the first argument.
-
-The functions in the Belt
libraryBelt
library included with Melange
-have been designed with the data-first convention in mind, so they work best
-with the pipe first operator.
-
-For example, we can rewrite the example above using `Belt.List.map` and the pipe
-first operator:
-
-```ocaml
-let sum_sq =
- [ 1; 2; 3 ]
- |. Belt.List.map square
- |. sum
-```
-```reasonml
-let sum_sq = [1, 2, 3]->(Belt.List.map(square))->sum;
-```
-
-We can see the difference on the error we get if the wrong function is passed to
-`Belt.List.map`:
-
-```ocaml
-let sum_sq =
- [ 1; 2; 3 ]
- |. Belt.List.map String.cat
- |. sum
-```
-```reasonml
-let sum_sq = [1, 2, 3]->(Belt.List.map(String.cat))->sum;
-```
-
-The compiler will show this error message:
-
-4 | |. Belt.List.map String.cat
- ^^^^^^^^^^
-Error: This expression has type string -> string -> string
- but an expression was expected of type int -> 'a
- Type string is not compatible with type int
-2 | let sum_sq = [1, 2, 3]->(Belt.List.map(String.cat))->sum;
- ^^^^^^^^^^
-Error: This expression has type string -> string -> string
- but an expression was expected of type int -> 'a
- Type string is not compatible with type int
-Some( Some .. Some (None))
Some(Some( .. Some(None)))
| internal representation |
-| Option.t Some 2
Some(2)
| `2` |
-| record {x = 1; y = 2}
{x: 1; y: 2}
| object `{x: 1, y: 2}` |
-| int64 | array of 2 elements `[high, low]` high is signed, low unsigned |
-| char | `'a'` -\> `97` (ascii code) |
-| bytes | number array |
-| list `[]` | `0` |
-| list \[ x; y \]
\[x, y\]
| `{ hd: x, tl: { hd: y, tl: 0 } }` |
-| variant | See below |
-| polymorphic variant | See below |
-
-Variants with a single non-nullary constructor:
-
-```ocaml
-type tree = Leaf | Node of int * tree * tree
-(* Leaf -> 0 *)
-(* Node(7, Leaf, Leaf) -> { _0: 7, _1: 0, _2: 0 } *)
-```
-```reasonml
-type tree =
- | Leaf
- | Node(int, tree, tree);
-/* Leaf -> 0 */
-/* Node(7, Leaf, Leaf) -> { _0: 7, _1: 0, _2: 0 } */
-```
-
-Variants with more than one non-nullary constructor:
-
-```ocaml
-type t = A of string | B of int
-(* A("foo") -> { TAG: 0, _0: "Foo" } *)
-(* B(2) -> { TAG: 1, _0: 2 } *)
-```
-```reasonml
-type t =
- | A(string)
- | B(int);
-/* A("foo") -> { TAG: 0, _0: "Foo" } */
-/* B(2) -> { TAG: 1, _0: 2 } */
-```
-
-Polymorphic variants:
-
-```ocaml
-let u = `Foo (* "Foo" *)
-let v = `Foo(2) (* { NAME: "Foo", VAL: "2" } *)
-```
-```reasonml
-let u = `Foo; /* "Foo" */
-let v = `Foo(2); /* { NAME: "Foo", VAL: "2" } */
-```
-
-Let’s see now some of these types in detail. We will first describe the [shared
-types](#shared-types), which have a transparent representation as JavaScript
-values, and then go through the [non-shared types](#non-shared-data-types), that
-have more complex runtime representations.
-
-> **_NOTE:_** Relying on the non-shared data types runtime representations by
-> reading or writing them manually from JavaScript code that communicates with
-> Melange code might lead to runtime errors, as these representations might
-> change in the future.
-
-### Shared types
-
-The following are types that can be shared between Melange and JavaScript almost
-"as is". Specific caveats are mentioned on the sections where they apply.
-
-#### Strings
-
-JavaScript strings are immutable sequences of UTF-16 encoded Unicode text. OCaml
-strings are immutable sequences of bytes and nowadays assumed to be UTF-8
-encoded text when interpreted as textual content. This is problematic when
-interacting with JavaScript code, because if one tries to use some unicode
-characters, like:
-
-```ocaml
-let () = Js.log "你好"
-```
-```reasonml
-let () = Js.log("你好");
-```
-
-It will lead to some cryptic console output. To rectify this, Melange allows to
-define [quoted string
-literals](https://v2.ocaml.org/manual/lex.html#sss:stringliterals) using the
-`js` identifier, for example:
-
-```ocaml
-let () = Js.log {js|你好,
-世界|js}
-```
-```reasonml
-let () = Js.log({js|你好,
-世界|js});
-```
-
-For convenience, Melange exposes another special quoted string identifier: `j`.
-It is similar to JavaScript’ string interpolation, but for variables only (not
-arbitrary expressions):
-
-```ocaml
-let world = {j|世界|j}
-let helloWorld = {j|你好,$world|j}
-```
-```reasonml
-let world = {j|世界|j};
-let helloWorld = {j|你好,$world|j};
-```
-
-You can surround the interpolation variable in parentheses too: `{j|你
-好,$(world)|j}`.
-
-To work with strings, the Melange standard library provides some utilities in
-the Stdlib.String
moduleStdlib.String
module. The
-bindings to the native JavaScript functions to work with strings are in the Js.String
moduleJs.String
module.
-
-#### Floating-point numbers
-
-OCaml floats are [IEEE
-754](https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64)
-with a 53-bit mantissa and exponents from -1022 to 1023. This happens to be the
-same encoding as [JavaScript
-numbers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding),
-so values of these types can be used transparently between Melange code and
-JavaScript code. The Melange standard library provides a Stdlib.Float
-moduleStdlib.Float
module. The
-bindings to the JavaScript APIs that manipulate float values can be found in the
-Js.Float
moduleJs.Float
module.
-
-#### Integers
-
-In Melange, integers are limited to 32 bits due to the [fixed-width
-conversion](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#fixed-width_number_conversion)
-of bitwise operations in JavaScript. While Melange integers compile to
-JavaScript numbers, treating them interchangeably can result in unexpected
-behavior due to differences in precision. Even though bitwise operations in
-JavaScript are constrained to 32 bits, integers themselves are represented using
-the same floating-point format [as
-numbers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding),
-allowing for a larger range of representable integers in JavaScript compared to
-Melange. When dealing with large numbers, it is advisable to use floats instead.
-For instance, floats are used in bindings like `Js.Date`.
-
-The Melange standard library provides a Stdlib.Int
moduleStdlib.Int
module. The
-bindings to work with JavaScript integers are in the Js.Int
moduleJs.Int
module.
-
-#### Arrays
-
-Melange arrays compile to JavaScript arrays. But note that unlike JavaScript
-arrays, all the values in a Melange array need to have the same type.
-
-Another difference is that OCaml arrays are fixed-sized, but on Melange side
-this constraint is relaxed. You can change an array’s length using functions
-like `Js.Array.push`, available in the bindings to the JavaScript APIs in the Js.Array
moduleJs.Array
module.
-
-Melange standard library also has a module to work with arrays, available in the
-Stdlib.Array
moduleStdlib.Array
module.
-
-#### Tuples
-
-OCaml tuples are compiled to JavaScript arrays. This is convenient when writing
-bindings that will use a JavaScript array with heterogeneous values, but that
-happens to have a fixed length.
-
-As a real world example of this can be found in
-[ReasonReact](https://github.com/reasonml/reason-react/), the Melange bindings
-for [React](https://react.dev/). In these bindings, component effects
-dependencies are represented as OCaml tuples, so they get compiled cleanly to
-JavaScript arrays by Melange.
-
-For example, some code like this:
-
-```ocaml
-let () = React.useEffect2 (fun () -> None) (foo, bar)
-```
-```reasonml
-let () = React.useEffect2(() => None, (foo, bar));
-```
-
-Will produce:
-
-```javascript
-React.useEffect(function () {}, [foo, bar]);
-```
-
-#### Booleans
-
-Values of type `bool` compile to JavaScript booleans.
-
-#### Records
-
-Melange records map directly to JavaScript objects. If the record fields include
-non-shared data types (like variants), these values should be transformed
-separately, and not be directly used in JavaScript.
-
-Extensive documentation about interfacing with JavaScript objects using records
-can be found in [the section below](#bind-to-js-object).
-
-#### Regular expressions
-
-Regular expressions created using the `%mel.re` extension node compile to their
-JavaScript counterpart.
-
-For example:
-
-```ocaml
-let r = [%mel.re "/b/g"]
-```
-```reasonml
-let r = [%mel.re "/b/g"];
-```
-
-Will compile to:
-
-```js
-var r = /b/g;
-```
-
-A regular expression like the above is of type `Js.Re.t`. The Js.Re
moduleJs.Re
module provides the
-bindings to the JavaScript functions that operate over regular expressions.
-
-## Non-shared data types
-
-The following types differ too much between Melange and JavaScript, so while
-they can always be manipulated from JavaScript, it is recommended to transform
-them before doing so.
-
-- Variants and polymorphic variants: Better transform them into readable
- JavaScript values before manipulating them from JavaScript, Melange provides
- [some helpers](#generate-getters-setters-and-constructors) to do so.
-- Exceptions
-- Option (a variant type): Better use the `Js.Nullable.fromOption` and
- `Js.Nullable.toOption` functions in the Js.Nullable
moduleJs.Nullable
module to
- transform them into either `null` or `undefined` values.
-- List (also a variant type): use `Array.of_list` and `Array.to_list` in the Stdlib.Array
moduleStdlib.Array
module.
-- Character
-- Int64
-- Lazy values
-
-## List of attributes and extension nodes
-
-**Attributes:**
-
-These attributes are used to annotate `external` definitions:
-
-- [`mel.get`](#bind-to-object-properties): read JavaScript object properties
- statically by name, using the dot notation `.`
-- [`mel.get_index`](#bind-to-object-properties): read a JavaScript object’s
- properties dynamically by using the bracket notation `[]`
-- [`mel.module`](#using-functions-from-other-javascript-modules): bind to a
- value from a JavaScript module
-- [`mel.new`](#javascript-classes): bind to a JavaScript class constructor
-- [`mel.obj`](#using-external-functions): create a JavaScript object
-- [`mel.return`](#wrapping-returned-nullable-values): automate conversion from
- nullable values to `Option.t` values
-- [`mel.send`](#calling-an-object-method): call a JavaScript object method using
- [pipe first](#pipe-first) convention
-- [`mel.send.pipe`](#calling-an-object-method): call a JavaScript object method
- using [pipe last](#pipe-last) convention
-- [`mel.set`](#bind-to-object-properties): set JavaScript object properties
- statically by name, using the dot notation `.`
-- [`mel.set_index`](#bind-to-object-properties): set JavaScript object
- properties dynamically by using the bracket notation `[]`
-- [`mel.scope`](#binding-to-properties-inside-a-module-or-global): reach to
- deeper properties inside a JavaScript object
-- [`mel.splice`](#variadic-function-arguments): a deprecated attribute, is an
- alternate form of `mel.variadic`
-- [`mel.variadic`](#variadic-function-arguments): bind to a function taking
- variadic arguments from an array
-
-These attributes are used to annotate arguments in `external` definitions:
-
-- [`u`](#binding-to-callbacks): define function arguments as uncurried (manual)
-- [`mel.int`](#using-polymorphic-variants-to-bind-to-enums): compile function
- argument to an int
-- [`mel.string`](#using-polymorphic-variants-to-bind-to-enums): compile function
- argument to a string
-- [`mel.this`](#modeling-this-based-callbacks): bind to `this` based callbacks
-- [`mel.uncurry`](#binding-to-callbacks): define function arguments as uncurried
- (automated)
-- [`mel.unwrap`](#approach-2-polymorphic-variant-mel-unwrap): unwrap variant
- values
-
-These attributes are used in places like records, fields, arguments, functions,
-and more:
-
-- `mel.as`: redefine the name generated in the JavaScript output code. Used in
- [constant function arguments](#constant-values-as-arguments),
- [variants](#conversion-functions), polymorphic variants (either [inlined in
- external functions](#using-polymorphic-variants-to-bind-to-enums) or [in type
- definitions](#polymorphic-variants)) and [record
- fields](#objects-with-static-shape-record-like).
-- [`deriving`](#generate-getters-setters-and-constructors): generate getters and
- setters for some types
-- [`mel.inline`](#inlining-constant-values): forcefully inline constant values
-- [`optional`](#generate-javascript-objects-with-optional-properties):
- translates optional fields in a record to omitted properties in the generated
- JavaScript object (combines with `deriving`)
-
-**Extension nodes:**
-
-In order to use any of these extension nodes, you will have to add the melange
-PPX preprocessor to your project. To do so, add the following to the `dune`
-file:
-
-```dune
-(library
- (name lib)
- (modes melange)
- (preprocess
- (pps melange.ppx)))
-```
-
-The same field `preprocess` can be added to `melange.emit`.
-
-Here is the list of all the extension nodes supported by Melange:
-
-- [`mel.debugger`](#debugger): insert `debugger` statements
-- [`mel.external`](#detect-global-variables): read global values
-- [`mel.obj`](#using-jst-objects): create JavaScript object literals
-- [`mel.raw`](#generate-raw-javascript): write raw JavaScript code
-- [`mel.re`](#regular-expressions): insert regular expressions
-
-## Generate raw JavaScript
-
-It is possible to directly write JavaScript code from a Melange file. This is
-unsafe, but it can be useful for prototyping or as an escape hatch.
-
-To do it, we will use the `mel.raw`
-[extension](https://v2.ocaml.org/manual/extensionnodes.html):
-
-```ocaml
-let add = [%mel.raw {|
- function(a, b) {
- console.log("hello from raw JavaScript!");
- return a + b;
- }
-|}]
-
-let () = Js.log (add 1 2)
-```
-```reasonml
-let add = [%mel.raw
- {|
- function(a, b) {
- console.log("hello from raw JavaScript!");
- return a + b;
- }
-|}
-];
-
-let () = Js.log(add(1, 2));
-```
-
-The `{||}` strings are called ["quoted
-strings"](https://ocaml.org/manual/lex.html#quoted-string-id). They are similar
-to JavaScript’s template literals, in the sense that they are multi-line, and
-there is no need to escape characters inside the string.
-
-Using one percentage signthe extension name between square brackets
-(`[%mel.raw 'a Option.t
Option.t('a)
, meaning its wrapped value is
-compatible with any type. If you use the value, it is recommended to annotate it
-into a known type first to avoid runtime issues.
-
-## Inlining constant values
-
-Some JavaScript idioms require special constants to be inlined since they serve
-as de-facto directives for bundlers. A common example is `process.env.NODE_ENV`:
-
-```js
-if (process.env.NODE_ENV !== "production") {
- // Development-only code
-}
-```
-
-becomes:
-
-```js
-if ("development" !== "production") {
- // Development-only code
-}
-```
-
-In this case, bundlers such as Webpack can tell that the `if` statement always
-evaluates to a specific branch and eliminate the others.
-
-Melange provides the `mel.inline` attribute to achieve the same goal in
-generated JavaScript. Let’s look at an example:
-
-```ocaml
-external node_env : string = "NODE_ENV" [@@mel.scope "process", "env"]
-
-let development = "development"
-let () = if node_env <> development then Js.log "Only in Production"
-
-let development_inline = "development" [@@mel.inline]
-let () = if node_env <> development_inline then Js.log "Only in Production"
-```
-```reasonml
-[@mel.scope ("process", "env")] external node_env: string = "NODE_ENV";
-
-let development = "development";
-let () =
- if (node_env != development) {
- Js.log("Only in Production");
- };
-
-[@mel.inline]
-let development_inline = "development";
-let () =
- if (node_env != development_inline) {
- Js.log("Only in Production");
- };
-```
-
-As we can see in the generated JavaScript presented below:
-
-- the `development` variable is emitted
- - it gets used as a variable `process.env.NODE_ENV !== development` in the
- `if` statement
-- the `development_inline` variable isn’t present in the final output
- - its value is inlined in the `if` statement: `process.env.NODE_ENV !==
- "development"`
-
-```js
-var development = "development";
-
-if (process.env.NODE_ENV !== development) {
- console.log("Only in Production");
-}
-
-if (process.env.NODE_ENV !== "development") {
- console.log("Only in Production");
-}
-```
-
-## Bind to JavaScript objects
-
-JavaScript objects are used in a variety of use cases:
-
-- As a fixed shape
- [record](https://en.wikipedia.org/wiki/Record_\(computer_science\)).
-- As a map or dictionary.
-- As a class.
-- As a module to import/export.
-
-Melange separates the binding methods for JavaScript objects based on these four
-use cases. This section documents the first three. Binding to JavaScript module
-objects is described in the ["Using functions from other JavaScript
-modules"](#using-functions-from-other-javascript-modules) section.
-
-
-
-### Objects with static shape (record-like)
-
-#### Using OCaml records
-
-If your JavaScript object has fixed fields, then it’s conceptually like an
-[OCaml
-record](https://v2.ocaml.org/manual/coreexamples.html#s%3Atut-recvariants).
-Since Melange compiles records into JavaScript objects, the most common way to
-bind to JavaScript objects is using records.
-
-```ocaml
-type person = {
- name : string;
- friends : string array;
- age : int;
-}
-
-external john : person = "john" [@@mel.module "MySchool"]
-let john_name = john.name
-```
-```reasonml
-type person = {
- name: string,
- friends: array(string),
- age: int,
-};
-
-[@mel.module "MySchool"] external john: person = "john";
-let john_name = john.name;
-```
-
-This is the generated JavaScript:
-
-```js
-var MySchool = require("MySchool");
-
-var john_name = MySchool.john.name;
-```
-
-External functions are documented in [a previous section](#external-functions).
-The `mel.module` attribute is documented
-[here](#using-functions-from-other-javascript-modules).
-
-If you want or need to use different field names on the Melange and the
-JavaScript sides, you can use the `mel.as` decorator:
-
-```ocaml
-type action = {
- type_ : string [@mel.as "type"]
-}
-
-let action = { type_ = "ADD_USER" }
-```
-```reasonml
-type action = {
- [@mel.as "type"]
- type_: string,
-};
-
-let action = {type_: "ADD_USER"};
-```
-
-Which generates the JavaScript code:
-
-```js
-var action = {
- type: "ADD_USER"
-};
-```
-
-This is useful to map to JavaScript attribute names that cannot be expressed in
-Melange, for example, where the JavaScript name we want to generate is a
-[reserved keyword](https://v2.ocaml.org/manual/lex.html#sss:keywords).
-
-It is also possible to map a Melange record to a JavaScript array by passing
-indices to the `mel.as` decorator:
-
-```ocaml
-type t = {
- foo : int; [@mel.as "0"]
- bar : string; [@mel.as "1"]
-}
-
-let value = { foo = 7; bar = "baz" }
-```
-```reasonml
-type t = {
- [@mel.as "0"]
- foo: int,
- [@mel.as "1"]
- bar: string,
-};
-
-let value = {
- foo: 7,
- bar: "baz",
-};
-```
-
-And its JavaScript generated code:
-
-```js
-var value = [
- 7,
- "baz"
-];
-```
-
-#### Using `Js.t` objects
-
-Alternatively to records, Melange offers another type that can be used to
-produce JavaScript objects. This type is 'a
-Js.t
Js.t('a)
, where `'a` is an [OCaml
-object](https://v2.ocaml.org/manual/objectexamples.html).
-
-The advantage of objects versus records is that no type declaration is needed in
-advance, which can be helpful for prototyping or quickly generating JavaScript
-object literals.
-
-Melange provides some ways to create `Js.t` object values, as well as accessing
-the properties inside them. To create values, the `[%mel.obj]` extension is
-used, and the `##` infix operator allows to read from the object properties:
-
-```ocaml
-let john = [%mel.obj { name = "john"; age = 99 }]
-let t = john##name
-```
-```reasonml
-let john = {
- "name": "john",
- "age": 99,
-};
-let t = john##name;
-```
-
-Which generates:
-
-```js
-var john = {
- name: "john",
- age: 99
-};
-
-var t = john.name;
-```
-
-Note that object types allow for some flexibility that the record types do not
-have. For example, an object type can be coerced to another with fewer values or
-methods, while it is impossible to coerce a record type to another one with
-fewer fields. So different object types that share some methods can be mixed in
-a data structure where only their common methods are visible.
-
-To give an example, one can create a function that operates in all the object
-types that include a field `name` that is of type string, e.g.:
-
-```ocaml
-let name_extended obj = obj##name ^ " wayne"
-
-let one = name_extended [%mel.obj { name = "john"; age = 99 }]
-let two = name_extended [%mel.obj { name = "jane"; address = "1 infinite loop" }]
-```
-```reasonml
-let name_extended = obj => obj##name ++ " wayne";
-
-let one =
- name_extended({
- "name": "john",
- "age": 99,
- });
-let two =
- name_extended({
- "name": "jane",
- "address": "1 infinite loop",
- });
-```
-
-To read more about objects and polymorphism we recommend checking the [OCaml
-docs](https://ocaml.org/docs/objects) or the [OCaml
-manual](https://v2.ocaml.org/manual/objectexamples.html).
-
-#### Using external functions
-
-We have already explored one approach for creating JavaScript object literals by
-using [`Js.t` values and the `mel.obj` extension](#using-jst-objects).
-
-Melange additionally offers the `mel.obj` attribute, which can be used in
-combination with external functions to create JavaScript objects. When these
-functions are called, they generate objects with fields corresponding to the
-labeled arguments of the function.
-
-If any of these labeled arguments are defined as optional and omitted during
-function application, the resulting JavaScript object will exclude the
-corresponding fields. This allows to create runtime objects and control whether
-optional keys are emitted at runtime.
-
-For example, assuming we need to bind to a JavaScript object like this:
-
-```js
-var homeRoute = {
- type: "GET",
- path: "/",
- action: () => console.log("Home"),
- // options: ...
-};
-```
-
-The first three fields are required and the `options` field is optional. You can
-declare a binding function like:
-
-```ocaml
-external route :
- _type:string ->
- path:string ->
- action:(string list -> unit) ->
- ?options:< .. > ->
- unit ->
- _ = ""
- [@@mel.obj]
-```
-```reasonml
-[@mel.obj]
-external route:
- (
- ~_type: string,
- ~path: string,
- ~action: list(string) => unit,
- ~options: {..}=?,
- unit
- ) =>
- _;
-```
-
-Note that the empty string at the end of the function is used to make it
-syntactically valid. The value of this string is ignored by the compiler.
-
-Since there is an optional argument `options`, an additional unlabeled argument
-of type `unit` is included after it. It allows to omit the optional argument on
-function application. More information about labeled optional arguments can be
-found in the [OCaml
-manual](https://v2.ocaml.org/manual/lablexamples.html#s:optional-arguments).
-
-The return type of the function should be left unspecified using the wildcard
-type `_`. Melange will automatically infer the type of the resulting JavaScript
-object.
-
-In the route function, the `_type` argument starts with an underscore. When
-binding to JavaScript objects with fields that are reserved keywords in OCaml,
-Melange allows the use of an underscore prefix for the labeled arguments. The
-resulting JavaScript object will have the underscore removed from the field
-names. This is only required for the `mel.obj` attribute, while for other cases,
-the `mel.as` attribute can be used to rename fields.
-
-If we call the function like this:
-
-```ocaml
-let homeRoute = route ~_type:"GET" ~path:"/" ~action:(fun _ -> Js.log "Home") ()
-```
-```reasonml
-let homeRoute =
- route(~_type="GET", ~path="/", ~action=_ => Js.log("Home"), ());
-```
-
-We get the following JavaScript, which does not include the `options` field
-since its argument wasn’t present:
-
-```javascript
-var homeRoute = {
- type: "GET",
- path: "/",
- action: (function (param) {
- console.log("Home");
- })
-};
-```
-
-#### Bind to object properties
-
-If you need to bind only to the property of a JavaScript object, you can use
-`mel.get` and `mel.set` to access it using the dot notation `.`:
-
-```ocaml
-(* Abstract type for the `document` value *)
-type document
-
-external document : document = "document"
-
-external set_title : document -> string -> unit = "title" [@@mel.set]
-external get_title : document -> string = "title" [@@mel.get]
-
-let current = get_title document
-let () = set_title document "melange"
-```
-```reasonml
-/* Abstract type for the `document` value */
-type document;
-
-external document: document = "document";
-
-[@mel.set] external set_title: (document, string) => unit = "title";
-[@mel.get] external get_title: document => string = "title";
-
-let current = get_title(document);
-let () = set_title(document, "melange");
-```
-
-This generates:
-
-```javascript
-var current = document.title;
-document.title = "melange";
-```
-
-Alternatively, if some dynamism is required on the way the property is accessed,
-you can use `mel.get_index` and `mel.set_index` to access it using the bracket
-notation `[]`:
-
-```ocaml
-type t
-external create : int -> t = "Int32Array" [@@mel.new]
-external get : t -> int -> int = "" [@@mel.get_index]
-external set : t -> int -> int -> unit = "" [@@mel.set_index]
-
-let () =
- let i32arr = (create 3) in
- set i32arr 0 42;
- Js.log (get i32arr 0)
-```
-```reasonml
-type t;
-[@mel.new] external create: int => t = "Int32Array";
-[@mel.get_index] external get: (t, int) => int;
-[@mel.set_index] external set: (t, int, int) => unit;
-
-let () = {
- let i32arr = create(3);
- set(i32arr, 0, 42);
- Js.log(get(i32arr, 0));
-};
-```
-
-Which generates:
-
-```js
-var i32arr = new Int32Array(3);
-i32arr[0] = 42;
-console.log(i32arr[0]);
-```
-
-### Objects with dynamic shape (dictionary-like)
-
-Sometimes JavaScript objects are used as dictionaries. In these cases:
-
-- All values stored in the object belong to the same type
-- Key-value pairs can be added or removed at runtime
-
-For this particular use case of JavaScript objects, Melange exposes a specific
-type `Js.Dict.t`. The values and functions to work with values of this type are
-defined in the Js.Dict
moduleJs.Dict
module, with operations
-like `get`, `set`, etc.
-
-Values of the type `Js.Dict.t` compile to JavaScript objects.
-
-### JavaScript classes
-
-JavaScript classes are special kinds of objects. To interact with classes,
-Melange exposes `mel.new` to emulate e.g. `new Date()`:
-
-```ocaml
-type t
-external create_date : unit -> t = "Date" [@@mel.new]
-let date = create_date ()
-```
-```reasonml
-type t;
-[@mel.new] external create_date: unit => t = "Date";
-let date = create_date();
-```
-
-Which generates:
-
-```js
-var date = new Date();
-```
-
-You can chain `mel.new` and `mel.module` if the JavaScript class you want to
-work with is in a separate JavaScript module:
-
-```ocaml
-type t
-external book : unit -> t = "Book" [@@mel.new] [@@mel.module]
-let myBook = book ()
-```
-```reasonml
-type t;
-[@mel.new] [@mel.module] external book: unit => t = "Book";
-let myBook = book();
-```
-
-Which generates:
-
-```js
-var Book = require("Book");
-var myBook = new Book();
-```
-
-## Bind to JavaScript functions or values
-
-### Using global functions or values
-
-Binding to a JavaScript function available globally makes use of `external`,
-like with objects. But unlike objects, there is no need to add any attributes:
-
-```ocaml
-(* Abstract type for `timeoutId` *)
-type timeoutId
-external setTimeout : (unit -> unit) -> int -> timeoutId = "setTimeout"
-external clearTimeout : timeoutId -> unit = "clearTimeout"
-
-let id = setTimeout (fun () -> Js.log "hello") 100
-let () = clearTimeout id
-```
-```reasonml
-/* Abstract type for `timeoutId` */
-type timeoutId;
-external setTimeout: (unit => unit, int) => timeoutId = "setTimeout";
-external clearTimeout: timeoutId => unit = "clearTimeout";
-
-let id = setTimeout(() => Js.log("hello"), 100);
-let () = clearTimeout(id);
-```
-
-> **_NOTE:_** The bindings to `setTimeout` and `clearTimeout` are shown here for
-> learning purposes, but they are already available in the target="_self" href="./api/ml/melange/Js/Global">Js.Global
-> module href="./api/re/melange/Js/Global">Js.Global
module.
-
-Generates:
-
-```javascript
-var id = setTimeout(function (param) {
- console.log("hello");
-}, 100);
-
-clearTimeout(id);
-```
-
-Global bindings can also be applied to values:
-
-```ocaml
-(* Abstract type for `document` *)
-type document
-
-external document : document = "document"
-let document = document
-```
-```reasonml
-/* Abstract type for `document` */
-type document;
-
-external document: document = "document";
-let document = document;
-```
-
-Which generates:
-
-```javascript
-var doc = document;
-```
-
-### Using functions from other JavaScript modules
-
-`mel.module` allows to bind to values that belong to another JavaScript module.
-It accepts a string with the name of the module, or the relative path to it.
-
-```ocaml
-external dirname : string -> string = "dirname" [@@mel.module "path"]
-let root = dirname "/User/github"
-```
-```reasonml
-[@mel.module "path"] external dirname: string => string = "dirname";
-let root = dirname("/User/github");
-```
-
-Generates:
-
-```js
-var Path = require("path");
-var root = Path.dirname("/User/github");
-```
-
-### Binding to properties inside a module or global
-
-For cases when we need to create bindings for a property within a module or a
-global JavaScript object, Melange provides the `mel.scope` attribute.
-
-For example, if we want to write some bindings for a specific property
-`commands` from [the `vscode`
-package](https://code.visualstudio.com/api/references/vscode-api#commands), we
-can do:
-
-```ocaml
-type param
-external executeCommands : string -> param array -> unit = ""
- [@@mel.scope "commands"] [@@mel.module "vscode"] [@@mel.variadic]
-
-let f a b c = executeCommands "hi" [| a; b; c |]
-```
-```reasonml
-type param;
-[@mel.scope "commands"] [@mel.module "vscode"] [@mel.variadic]
-external executeCommands: (string, array(param)) => unit;
-
-let f = (a, b, c) => executeCommands("hi", [|a, b, c|]);
-```
-
-Which compiles to:
-
-```javascript
-var Vscode = require("vscode");
-
-function f(a, b, c) {
- Vscode.commands.executeCommands("hi", a, b, c);
-}
-```
-
-The `mel.scope` attribute can take multiple arguments as payload, in case we
-want to reach deeper into the object from the module we are importing.
-
-For example:
-
-```ocaml
-type t
-
-external back : t = "back"
- [@@mel.module "expo-camera"] [@@mel.scope "Camera", "Constants", "Type"]
-
-let camera_type_back = back
-```
-```reasonml
-type t;
-
-[@mel.module "expo-camera"] [@mel.scope ("Camera", "Constants", "Type")]
-external back: t = "back";
-
-let camera_type_back = back;
-```
-
-Which generates:
-
-```javascript
-var ExpoCamera = require("expo-camera");
-
-var camera_type_back = ExpoCamera.Camera.Constants.Type.back;
-```
-
-It can be used without `mel.module`, to created scoped bindings to global
-values:
-
-```ocaml
-external imul : int -> int -> int = "imul" [@@mel.scope "Math"]
-
-let res = imul 1 2
-```
-```reasonml
-[@mel.scope "Math"] external imul: (int, int) => int = "imul";
-
-let res = imul(1, 2);
-```
-
-Which produces:
-
-```javascript
-var res = Math.imul(1, 2);
-```
-
-Or it can be used together with `mel.new`:
-
-```ocaml
-type t
-
-external create : unit -> t = "GUI"
- [@@mel.new] [@@mel.scope "default"] [@@mel.module "dat.gui"]
-
-let gui = create ()
-```
-```reasonml
-type t;
-
-[@mel.new] [@mel.scope "default"] [@mel.module "dat.gui"]
-external create: unit => t = "GUI";
-
-let gui = create();
-```
-
-Which generates:
-
-
-```javascript
-var DatGui = require("dat.gui");
-
-var gui = new (DatGui.default.GUI)();
-```
-
-### Labeled arguments
-
-OCaml has [labeled arguments](https://v2.ocaml.org/manual/lablexamples.html),
-which can also be optional, and work with `external` as well.
-
-Labeled arguments can be useful to provide more information about the arguments
-of a JavaScript function that is called from Melange.
-
-Let’s say we have the following JavaScript function, that we want to call from
-Melange:
-
-```js
-// MyGame.js
-
-function draw(x, y, border) {
- // let’s assume `border` is optional and defaults to false
-}
-draw(10, 20)
-draw(20, 20, true)
-```
-
-When writing Melange bindings, we can add labeled arguments to make things more
-clear:
-
-```ocaml
-external draw : x:int -> y:int -> ?border:bool -> unit -> unit = "draw"
- [@@mel.module "MyGame"]
-
-let () = draw ~x:10 ~y:20 ~border:true ()
-let () = draw ~x:10 ~y:20 ()
-```
-```reasonml
-[@mel.module "MyGame"]
-external draw: (~x: int, ~y: int, ~border: bool=?, unit) => unit = "draw";
-
-let () = draw(~x=10, ~y=20, ~border=true, ());
-let () = draw(~x=10, ~y=20, ());
-```
-
-Generates:
-
-```js
-var MyGame = require("MyGame");
-
-MyGame.draw(10, 20, true);
-MyGame.draw(10, 20, undefined);
-```
-
-The generated JavaScript function is the same, but now the usage in Melange is
-much clearer.
-
-**Note**: in this particular case, a final param of type unit, `()` must be
-added after `border`, since `border` is an optional argument at the last
-position. Not having the last param `unit` would lead to a warning, which is
-explained in detail [in the OCaml
-documentation](https://ocaml.org/docs/labels#warning-this-optional-argument-cannot-be-erased).
-
-Note that you can freely reorder the labeled arguments when applying the
-function on the Melange side. The generated code will maintain the original
-order that was used when declaring the function:
-
-```ocaml
-external draw : x:int -> y:int -> ?border:bool -> unit -> unit = "draw"
- [@@mel.module "MyGame"]
-let () = draw ~x:10 ~y:20 ()
-let () = draw ~y:20 ~x:10 ()
-```
-```reasonml
-[@mel.module "MyGame"]
-external draw: (~x: int, ~y: int, ~border: bool=?, unit) => unit = "draw";
-let () = draw(~x=10, ~y=20, ());
-let () = draw(~y=20, ~x=10, ());
-```
-
-Generates:
-
-```js
-var MyGame = require("MyGame");
-
-MyGame.draw(10, 20, undefined);
-MyGame.draw(10, 20, undefined);
-```
-
-### Calling an object method
-
-If we need to call a JavaScript method, Melange provides the attribute
-`mel.send`.
-
-> In the following snippets, we will be referring to a type `Dom.element`, which
-> is provided within the library `melange.dom`. You can add it to your project
-> by including `(libraries melange.dom)` to your `dune` file:
-
-```ocaml
-(* Abstract type for the `document` global *)
-type document
-
-external document : document = "document"
-external get_by_id : document -> string -> Dom.element = "getElementById"
- [@@mel.send]
-
-let el = get_by_id document "my-id"
-```
-```reasonml
-/* Abstract type for the `document` global */
-type document;
-
-external document: document = "document";
-[@mel.send]
-external get_by_id: (document, string) => Dom.element = "getElementById";
-
-let el = get_by_id(document, "my-id");
-```
-
-Generates:
-
-```js
-var el = document.getElementById("my-id");
-```
-
-When using `mel.send`, the first argument will be the object that holds the
-property with the function we want to call. This combines well with the pipe
-first operator \|.
\-\>
, see the ["Chaining"](#chaining) section
-below.
-
-If we want to design our bindings to be used with OCaml pipe last operator `|>`,
-there is an alternate `mel.send.pipe` attribute. Let’s rewrite the example above
-using it:
-
-```ocaml
-(* Abstract type for the `document` global *)
-type document
-
-external document : document = "document"
-external get_by_id : string -> Dom.element = "getElementById"
- [@@mel.send.pipe: document]
-
-let el = get_by_id "my-id" document
-```
-```reasonml
-/* Abstract type for the `document` global */
-type document;
-
-external document: document = "document";
-[@mel.send.pipe: document]
-external get_by_id: string => Dom.element = "getElementById";
-
-let el = get_by_id("my-id", document);
-```
-
-Generates the same code as `mel.send`:
-
-```js
-var el = document.getElementById("my-id");
-```
-
-#### Chaining
-
-It is common to find this kind of API in JavaScript: `foo().bar().baz()`. This
-kind of API can be designed with Melange externals. Depending on which
-convention we want to use, there are two attributes available:
-
-- For a data-first convention, the `mel.send` attribute, in combination with
- [the pipe first operator](#pipe-first) \|.
\-\>
-- For a data-last convention, the `mel.send.pipe` attribute, in combination with
- OCaml [pipe last operator](#pipe-last) `|>`.
-
-Let’s see first an example of chaining using data-first convention with the pipe
-first operator \|.
\-\>
:
-
-```ocaml
-(* Abstract type for the `document` global *)
-type document
-
-external document : document = "document"
-external get_by_id : document -> string -> Dom.element = "getElementById"
- [@@mel.send]
-external get_by_classname : Dom.element -> string -> Dom.element
- = "getElementsByClassName"
- [@@mel.send]
-
-let el = document |. get_by_id "my-id" |. get_by_classname "my-class"
-```
-```reasonml
-/* Abstract type for the `document` global */
-type document;
-
-external document: document = "document";
-[@mel.send]
-external get_by_id: (document, string) => Dom.element = "getElementById";
-[@mel.send]
-external get_by_classname: (Dom.element, string) => Dom.element =
- "getElementsByClassName";
-
-let el = document->(get_by_id("my-id"))->(get_by_classname("my-class"));
-```
-
-Will generate:
-
-```javascript
-var el = document.getElementById("my-id").getElementsByClassName("my-class");
-```
-
-Now with pipe last operator `|>`:
-
-```ocaml
-(* Abstract type for the `document` global *)
-type document
-
-external document : document = "document"
-external get_by_id : string -> Dom.element = "getElementById"
- [@@mel.send.pipe: document]
-external get_by_classname : string -> Dom.element = "getElementsByClassName"
- [@@mel.send.pipe: Dom.element]
-
-let el = document |> get_by_id "my-id" |> get_by_classname "my-class"
-```
-```reasonml
-/* Abstract type for the `document` global */
-type document;
-
-external document: document = "document";
-[@mel.send.pipe: document]
-external get_by_id: string => Dom.element = "getElementById";
-[@mel.send.pipe: Dom.element]
-external get_by_classname: string => Dom.element = "getElementsByClassName";
-
-let el = document |> get_by_id("my-id") |> get_by_classname("my-class");
-```
-
-Will generate the same JavaScript as the pipe first version:
-
-```javascript
-var el = document.getElementById("my-id").getElementsByClassName("my-class");
-```
-
-### Variadic function arguments
-
-Sometimes JavaScript functions take an arbitrary amount of arguments. For these
-cases, Melange provides the `mel.variadic` attribute, which can be attached to
-the `external` declaration. However, there is one caveat: all the variadic
-arguments need to belong to the same type.
-
-```ocaml
-external join : string array -> string = "join"
- [@@mel.module "path"] [@@mel.variadic]
-let v = join [| "a"; "b" |]
-```
-```reasonml
-[@mel.module "path"] [@mel.variadic]
-external join: array(string) => string = "join";
-let v = join([|"a", "b"|]);
-```
-
-Generates:
-
-```js
-var Path = require("path");
-var v = Path.join("a", "b");
-```
-
-If more dynamism is needed, there is a way to inject elements with different
-types in the array and still have Melange compile to JavaScript values that are
-not wrapped using the OCaml
-[`unboxed`](https://v2.ocaml.org/manual/attributes.html) attribute, which was
-mentioned [in the OCaml attributes section](#reusing-ocaml-attributes):
-
-```ocaml
-type hide = Hide : 'a -> hide [@@unboxed]
-
-external join : hide array -> string = "join" [@@mel.module "path"] [@@mel.variadic]
-
-let v = join [| Hide "a"; Hide 2 |]
-```
-```reasonml
-[@unboxed]
-type hide =
- | Hide('a): hide;
-
-[@mel.module "path"] [@mel.variadic]
-external join: array(hide) => string = "join";
-
-let v = join([|Hide("a"), Hide(2)|]);
-```
-
-Compiles to:
-
-```javascript
-var Path = require("path");
-
-var v = Path.join("a", 2);
-```
-
-### Bind to a polymorphic function
-
-Some JavaScript libraries will define functions where the arguments can vary on
-both type and shape. There are two approaches to bind to those, depending on how
-dynamic they are.
-
-#### Approach 1: Multiple external functions
-
-If it is possible to enumerate the many forms an overloaded JavaScript function
-can take, a flexible approach is to bind to each form individually:
-
-```ocaml
-external drawCat : unit -> unit = "draw" [@@mel.module "MyGame"]
-external drawDog : giveName:string -> unit = "draw" [@@mel.module "MyGame"]
-external draw : string -> useRandomAnimal:bool -> unit = "draw"
- [@@mel.module "MyGame"]
-```
-```reasonml
-[@mel.module "MyGame"] external drawCat: unit => unit = "draw";
-[@mel.module "MyGame"] external drawDog: (~giveName: string) => unit = "draw";
-[@mel.module "MyGame"]
-external draw: (string, ~useRandomAnimal: bool) => unit = "draw";
-```
-
-Note how all three externals bind to the same JavaScript function, `draw`.
-
-#### Approach 2: Polymorphic variant + `mel.unwrap`
-
-In some cases, the function has a constant number of arguments but the type of
-the argument can vary. For cases like this, we can model the argument as a
-variant and use the `mel.unwrap` attribute in the external.
-
-Let’s say we want to bind to the following JavaScript function:
-
-```js
-function padLeft(value, padding) {
- if (typeof padding === "number") {
- return Array(padding + 1).join(" ") + value;
- }
- if (typeof padding === "string") {
- return padding + value;
- }
- throw new Error(`Expected string or number, got '${padding}'.`);
-}
-```
-
-As the `padding` argument can be either a number or a string, we can use
-`mel.unwrap` to define it. It is important to note that `mel.unwrap` imposes
-certain requirements on the type it is applied to:
-
-- It needs to be a [polymorphic
- variant](https://v2.ocaml.org/manual/polyvariant.html)
-- Its definition needs to be inlined
-- Each variant tag needs to have an argument
-- The variant type can not be opened (can’t use `>`)
-
-```ocaml
-external padLeft:
- string
- -> ([ `Str of string
- | `Int of int
- ] [@mel.unwrap])
- -> string
- = "padLeft"
-
-let _ = padLeft "Hello World" (`Int 4)
-let _ = padLeft "Hello World" (`Str "Message from Melange: ")
-```
-```reasonml
-external padLeft:
- (
- string,
- [@mel.unwrap] [
- | `Str(string)
- | `Int(int)
- ]
- ) =>
- string =
- "padLeft";
-
-let _ = padLeft("Hello World", `Int(4));
-let _ = padLeft("Hello World", `Str("Message from Melange: "));
-```
-
-Which produces the following JavaScript:
-
-```js
-padLeft("Hello World", 4);
-padLeft("Hello World", "Message from Melange: ");
-```
-
-As we saw in the [Non-shared data types](#non-shared-data-types) section, we
-should rather avoid passing variants directly to the JavaScript side. By using
-`mel.unwrap` we get the best of both worlds: from Melange we can use variants,
-while JavaScript gets the raw values inside them.
-
-### Using polymorphic variants to bind to enums
-
-Some JavaScript APIs take a limited subset of values as input. For example,
-Node’s `fs.readFileSync` second argument can only take a few given string
-values: `"ascii"`, `"utf8"`, etc. Some other functions can take values from a
-few given integers, like the `createStatusBarItem` function in VS Code API,
-which can take an `alignment` parameter that can only be [`1` or
-`2`](https://github.com/Microsoft/vscode/blob/2362ec665c84a1519162b50c36ed4f29d1e20f62/src/vs/vscode.d.ts#L4098-L4109).
-
-One could still type these parameters as just `string` or `int`, but this would
-not prevent consumers of the external function from calling it using values that
-are unsupported by the JavaScript function. Let’s see how we can use polymorphic
-variants to avoid runtime errors.
-
-If the values are strings, we can use the `mel.string` attribute:
-
-```ocaml
-external read_file_sync :
- name:string -> ([ `utf8 | `ascii ][@mel.string]) -> string = "readFileSync"
- [@@mel.module "fs"]
-
-let _ = read_file_sync ~name:"xx.txt" `ascii
-```
-```reasonml
-[@mel.module "fs"]
-external read_file_sync:
- (
- ~name: string,
- [@mel.string] [
- | `utf8
- | `ascii
- ]
- ) =>
- string =
- "readFileSync";
-
-let _ = read_file_sync(~name="xx.txt", `ascii);
-```
-
-Which generates:
-
-```js
-var Fs = require("fs");
-Fs.readFileSync("xx.txt", "ascii");
-```
-
-This technique can be combined with the `mel.as` attribute to modify the strings
-produced from the polymorphic variant values. For example:
-
-```ocaml
-type document
-type style
-
-external document : document = "document"
-external get_by_id : document -> string -> Dom.element = "getElementById"
-[@@mel.send]
-external style : Dom.element -> style = "style" [@@mel.get]
-external transition_timing_function :
- style ->
- ([ `ease
- | `easeIn [@mel.as "ease-in"]
- | `easeOut [@mel.as "ease-out"]
- | `easeInOut [@mel.as "ease-in-out"]
- | `linear ]
- [@mel.string]) ->
- unit = "transitionTimingFunction"
-[@@mel.set]
-
-let element_style = style (get_by_id document "my-id")
-let () = transition_timing_function element_style `easeIn
-```
-```reasonml
-type document;
-type style;
-
-external document: document = "document";
-[@mel.send]
-external get_by_id: (document, string) => Dom.element = "getElementById";
-[@mel.get] external style: Dom.element => style = "style";
-[@mel.set]
-external transition_timing_function:
- (
- style,
- [@mel.string] [
- | `ease
- | [@mel.as "ease-in"] `easeIn
- | [@mel.as "ease-out"] `easeOut
- | [@mel.as "ease-in-out"] `easeInOut
- | `linear
- ]
- ) =>
- unit =
- "transitionTimingFunction";
-
-let element_style = style(get_by_id(document, "my-id"));
-let () = transition_timing_function(element_style, `easeIn);
-```
-
-This will generate:
-
-```javascript
-var element_style = document.getElementById("my-id").style;
-
-element_style.transitionTimingFunction = "ease-in";
-```
-
-Aside from producing string values, Melange also offers `mel.int` to produce
-integer values. `mel.int` can also be combined with `mel.as`:
-
-```ocaml
-external test_int_type :
- ([ `on_closed | `on_open [@mel.as 20] | `in_bin ][@mel.int]) -> int
- = "testIntType"
-
-let value = test_int_type `on_open
-```
-```reasonml
-external test_int_type:
- (
- [@mel.int]
- [
- | `on_closed
- | [@mel.as 20] `on_open
- | `in_bin
- ]
- ) =>
- int =
- "testIntType";
-
-let value = test_int_type(`on_open);
-```
-
-In this example, `on_closed` will be encoded as 0, `on_open` will be 20 due to
-the attribute `mel.as` and `in_bin` will be 21, because if no `mel.as`
-annotation is provided for a variant tag, the compiler continues assigning
-values counting up from the previous one.
-
-This code generates:
-
-```js
-var value = testIntType(20);
-```
-
-### Using polymorphic variants to bind to event listeners
-
-Polymorphic variants can also be used to wrap event listeners, or any other kind
-of callback, for example:
-
-```ocaml
-type readline
-
-external on :
- readline ->
- ([ `close of unit -> unit | `line of string -> unit ][@mel.string]) ->
- readline = "on"
- [@@mel.send]
-
-let register rl =
- rl |. on (`close (fun event -> ())) |. on (`line (fun line -> Js.log line))
-```
-```reasonml
-type readline;
-
-[@mel.send]
-external on:
- (
- readline,
- [@mel.string] [
- | `close(unit => unit)
- | `line(string => unit)
- ]
- ) =>
- readline =
- "on";
-
-let register = rl =>
- rl->(on(`close(event => ())))->(on(`line(line => Js.log(line))));
-```
-
-This generates:
-
-```js
-function register(rl) {
- return rl
- .on("close", function($$event) {})
- .on("line", function(line) {
- console.log(line);
- });
-}
-```
-
-### Constant values as arguments
-
-Sometimes we want to call a JavaScript function and make sure one of the
-arguments is always constant. For this, the `[@mel.as]` attribute can be
-combined with the wildcard pattern `_`:
-
-```ocaml
-external process_on_exit : (_[@mel.as "exit"]) -> (int -> unit) -> unit
- = "process.on"
-
-let () =
- process_on_exit (fun exit_code ->
- Js.log ("error code: " ^ string_of_int exit_code))
-```
-```reasonml
-external process_on_exit: ([@mel.as "exit"] _, int => unit) => unit =
- "process.on";
-
-let () =
- process_on_exit(exit_code =>
- Js.log("error code: " ++ string_of_int(exit_code))
- );
-```
-
-This generates:
-
-```js
-process.on("exit", function (exitCode) {
- console.log("error code: " + exitCode.toString());
-});
-```
-
-The `mel.as "exit"` and the wildcard `_` pattern together will tell Melange to
-compile the first argument of the JavaScript function to the string `"exit"`.
-
-You can also use any JSON literal by passing a quoted string to `mel.as`:
-`mel.as {json|true|json}` or `mel.as {json|{"name": "John"}|json}`.
-
-### Binding to callbacks
-
-In OCaml, all functions have arity 1. This means that if you define a function
-like this:
-
-```ocaml
-let add x y = x + y
-```
-```reasonml
-let add = (x, y) => x + y;
-```
-
-Its type will be `int -> int -> int`. This means that one can partially apply
-`add` by calling `add 1`, which will return another function expecting the
-second argument of the addition. This kind of functions are called "curried"
-functions, more information about currying in OCaml can be found in [this
-chapter](https://cs3110.github.io/textbook/chapters/hop/currying.html) of the
-"OCaml Programming: Correct + Efficient + Beautiful" book.
-
-This is incompatible with how function calling conventions work in JavaScript,
-where all function calls always apply all the arguments. To continue the
-example, let’s say we have an `add` function implemented in JavaScript, similar
-to the one above:
-
-```javascript
-var add = function (a, b) {
- return a + b;
-};
-```
-
-If we call `add(1)`, the function will be totally applied, with `b` having
-`undefined` value. And as JavaScript will try to add `1` with `undefined`, we
-will get `NaN` as a result.
-
-To illustrate this difference and how it affects Melange bindings, let’s say we
-want to write bindings for a JavaScript function like this:
-
-```javascript
-function map (a, b, f){
- var i = Math.min(a.length, b.length);
- var c = new Array(i);
- for(var j = 0; j < i; ++j){
- c[j] = f(a[i],b[i])
- }
- return c ;
-}
-```
-
-A naive external function declaration could be as below:
-
-```ocaml
-external map : 'a array -> 'b array -> ('a -> 'b -> 'c) -> 'c array = "map"
-```
-```reasonml
-external map: (array('a), array('b), ('a, 'b) => 'c) => array('c) = "map";
-```
-
-Unfortunately, this is not completely correct. The issue is in the callback
-function, with type `'a -> 'b -> 'c`. This means that `map` will expect a
-function like `add` described above. But as we said, in OCaml, having two
-arguments means just to have two functions that take one argument.
-
-Let’s rewrite `add` to make the problem a bit more clear:
-
-```ocaml
-let add x = let partial y = x + y in partial
-```
-```reasonml
-let add = x => {
- let partial = y => x + y;
- partial;
-};
-```
-
-This will be compiled to:
-
-```javascript
-function add(x) {
- return (function (y) {
- return x + y | 0;
- });
-}
-```
-
-Now if we ever used our external function `map` with our `add` function by
-calling `map arr1 arr2 add` it would not work as expected. JavaScript function
-application does not work the same as in OCaml, so the function call in the
-`map` implementation, `f(a[i],b[i])`, would be applied over the outer JavaScript
-function `add`, which only takes one argument `x`, and `b[i]` would be just
-discarded. The value returned from the operation would not be the addition of
-the two numbers, but rather the inner anonymous callback.
-
-To solve this mismatch between OCaml and JavaScript functions and their
-application, Melange provides a special attribute `@u` that can be used to
-annotate external functions that need to be "uncurried".
-
-In Reason syntax, this attribute does not need to be
-written explicitly, as it is deeply integrated with the Reason parser. To
-specify some function type as "uncurried", one just needs to add the dot
-character `.` to the function type. For example, `(. 'a, 'b) => 'c` instead of
-`('a, 'b) => 'c`.
-
-In the example above:
-
-```ocaml
-external map : 'a array -> 'b array -> (('a -> 'b -> 'c)[@u]) -> 'c array
- = "map"
-```
-```reasonml
-external map: (array('a), array('b), (. 'a, 'b) => 'c) => array('c) = "map";
-```
-
-Here `('a -> 'b -> 'c [@u])``(. 'a, 'b) => 'c`will be interpreted as having
-arity 2. In general, `'a0 -> 'a1 ... 'aN -> 'b0 [@u]`
-is the same as `'a0 -> 'a1 ... 'aN -> 'b0``.
-'a0, 'a1, ... 'aN => 'b0` is the same as `'a0, 'a1, ... 'aN => 'b0`
-except the former’s arity is guaranteed to be N while the latter is unknown.
-
-If we try now to call `map` using `add`:
-
-```ocaml
-let add x y = x + y
-let _ = map [||] [||] add
-```
-```reasonml
-let add = (x, y) => x + y;
-let _ = map([||], [||], add);
-```
-We will get an error:
-
-```text
-let _ = map [||] [||] add
- ^^^
-This expression has type int -> int -> int
-but an expression was expected of type ('a -> 'b -> 'c) Js.Fn.arity2
-```
-
-To solve this, we add `@u``.` in the function definition as well:
-
-```ocaml
-let add = fun [@u] x y -> x + y
-```
-```reasonml
-let add = (. x, y) => x + y;
-```
-
-Annotating function definitions can be quite cumbersome when writing a lot of
-externals.
-
-To work around the verbosity, Melange offers another attribute called
-`mel.uncurry`.
-
-Let’s see how we could use it in the previous example. We just need to replace
-`u` with `mel.uncurry`:
-
-```ocaml
-external map :
- 'a array -> 'b array -> (('a -> 'b -> 'c)[@mel.uncurry]) -> 'c array = "map"
-```
-```reasonml
-external map:
- (array('a), array('b), [@mel.uncurry] (('a, 'b) => 'c)) => array('c) =
- "map";
-```
-
-Now if we try to call `map` with a regular `add` function:
-
-```ocaml
-let add x y = x + y
-let _ = map [||] [||] add
-```
-```reasonml
-let add = (x, y) => x + y;
-let _ = map([||], [||], add);
-```
-
-Everything works fine now, without having to attach any attributes to `add`.
-
-The main difference between `u` and `mel.uncurry` is that the latter only works
-with externals. `mel.uncurry` is the recommended option to use for bindings,
-while `u` remains useful for those use cases where performance is crucial and we
-want the JavaScript functions generated from OCaml ones to not be applied
-partially.
-
-### Modeling `this`\-based Callbacks
-
-Many JavaScript libraries have callbacks which rely on the [`this`
-keyword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this),
-for example:
-
-```js
-x.onload = function(v) {
- console.log(this.response + v)
-}
-```
-
-Inside the `x.onload` callback, `this` would be pointing to `x`. It would not be
-correct to declare `x.onload` of type `unit -> unit`. Instead, Melange
-introduces a special attribute, `mel.this`, which allows to type `x` as this:
-
-```ocaml
-type x
-external x : x = "x"
-external set_onload : x -> ((x -> int -> unit)[@mel.this]) -> unit = "onload"
- [@@mel.set]
-external resp : x -> int = "response" [@@mel.get]
-let _ =
- set_onload x
- begin
- fun [@mel.this] o v -> Js.log (resp o + v)
- end
-```
-```reasonml
-type x;
-external x: x = "x";
-[@mel.set]
-external set_onload: (x, [@mel.this] ((x, int) => unit)) => unit = "onload";
-[@mel.get] external resp: x => int = "response";
-let _ = set_onload(x, [@mel.this] (o, v) => Js.log(resp(o) + v));
-```
-
-Which generates:
-
-```javascript
-x.onload = function (v) {
- var o = this;
- console.log((o.response + v) | 0);
-};
-```
-
-Note that the first argument will be reserved for `this`.
-
-### Wrapping returned nullable values
-
-JavaScript models `null` and `undefined` differently, whereas it can be useful
-to treat both as `'a option``option('a)` in Melange.
-
-Melange understands the `mel.return` attribute in externals to model how
-nullable return types should be wrapped at the bindings boundary. An `external`
-value with `mel.return` converts the return value to an `option` type, avoiding
-the need for extra wrapping / unwrapping with functions such as
-`Js.Nullable.toOption`.
-
-```ocaml
-type element
-type document
-external get_by_id : document -> string -> element option = "getElementById"
- [@@mel.send] [@@mel.return nullable]
-
-let test document =
- let elem = get_by_id document "header" in
- match elem with
- | None -> 1
- | Some _element -> 2
-```
-```reasonml
-type element;
-type document;
-[@mel.send] [@mel.return nullable]
-external get_by_id: (document, string) => option(element) = "getElementById";
-
-let test = document => {
- let elem = get_by_id(document, "header");
- switch (elem) {
- | None => 1
- | Some(_element) => 2
- };
-};
-```
-
-Which generates:
-
-```js
-function test($$document) {
- var elem = $$document.getElementById("header");
- if (elem == null) {
- return 1;
- } else {
- return 2;
- }
-}
-```
-
-The `mel.return` attribute takes an attribute payload, as seen with `[@@mel.return nullable]``[@mel.return nullable]` above. Currently 4
-directives are supported: `null_to_opt`, `undefined_to_opt`, `nullable` and
-`identity`.
-
-`nullable` is encouraged, as it will convert from `null` and `undefined` to
-`option` type.
-
-
-
-`identity` will make sure that compiler will do nothing about the returned
-value. It is rarely used, but introduced here for debugging purposes.
-
-## Generate getters, setters and constructors
-
-As we saw in a [previous section](#non-shared-data-types), there are some types
-in Melange that compile to values that are not easy to manipulate from
-JavaScript. To facilitate the communication from JavaScript code with values of
-these types, Melange includes an attribute `deriving` that helps generating
-conversion functions, as well as functions to create values from these types. In
-particular, for variants and polymorphic variants.
-
-Additionally, `deriving` can be used with record types, to generate setters and
-getters as well as creation functions.
-
-### Variants
-
-#### Creating values
-
-Use `@deriving accessors` on a variant type to create constructor values for
-each branch.
-
-```ocaml
-type action =
- | Click
- | Submit of string
- | Cancel
-[@@deriving accessors]
-```
-```reasonml
-[@deriving accessors]
-type action =
- | Click
- | Submit(string)
- | Cancel;
-```
-
-Melange will generate one `let` definition for each variant tag, implemented as
-follows:
-
-- For variant tags with payloads, it will be a function that takes the payload
- value as a parameter.
-- For variant tags without payloads, it will be a constant with the runtime
- value of the tag.
-
-Given the `action` type definition above, annotated with `deriving`, Melange
-will generate something similar to the following code:
-
-```ocaml
-type action =
- | Click
- | Submit of string
- | Cancel
-
-let click = (Click : action)
-let submit param = (Submit param : action)
-let cancel = (Cancel : action)
-```
-```reasonml
-type action =
- | Click
- | Submit(string)
- | Cancel;
-
-let click: action = Click;
-let submit = (param): action => Submit(param);
-let cancel: action = Cancel;
-```
-
-Which will result in the following JavaScript code after compilation:
-
-```javascript
-function submit(param_0) {
- return /* Submit */{
- _0: param_0
- };
-}
-
-var click = /* Click */0;
-
-var cancel = /* Cancel */1;
-```
-
-Note the generated definitions are lower-cased, and they can be safely used from
-JavaScript code. For example, if the above JavaScript generated code was located
-in a `generators.js` file, the definitions can be used like this:
-
-```javascript
-const generators = require('./generators.js');
-
-const hello = generators.submit("Hello");
-const click = generators.click;
-```
-
-#### Conversion functions
-
-Use `@deriving jsConverter` on a variant type to create converter functions that
-allow to transform back and forth between JavaScript integers and Melange
-variant values.
-
-There are a few differences with `@deriving accessors`:
-
-- `jsConverter` works with the `mel.as` attribute, while `accessors` does not
-- `jsConverter` does not support variant tags with payload, while `accessors`
- does
-- `jsConverter` generates functions to transform values back and forth, while
- `accessors` generates functions to create values
-
-Let’s see a version of the previous example, adapted to work with `jsConverter`
-given the constraints above:
-
-```ocaml
-type action =
- | Click
- | Submit [@mel.as 3]
- | Cancel
-[@@deriving jsConverter]
-```
-```reasonml
-[@deriving jsConverter]
-type action =
- | Click
- | [@mel.as 3] Submit
- | Cancel;
-```
-
-This will generate a couple of functions with the following types:
-
-```ocaml
-val actionToJs : action -> int
-
-val actionFromJs : int -> action option
-```
-```reasonml
-external actionToJs: action => int = ;
-
-external actionFromJs: int => option(action) = ;
-```
-
-`actionToJs` returns integers from values of `action` type. It will start with 0
-for `Click`, 3 for `Submit` (because it was annotated with `mel.as`), and then 4
-for `Cancel`, in the same way that we described when [using `mel.int` with
-polymorphic variants](#using-polymorphic-variants-to-bind-to-enums).
-
-`actionFromJs` returns a value of type `option`, because not every integer can
-be converted into a variant tag of the `action` type.
-
-##### Hide runtime types
-
-For extra type safety, we can hide the runtime representation of variants
-(`int`) from the generated functions, by using `jsConverter { newType }` payload
-with `@deriving`:
-
-```ocaml
-type action =
- | Click
- | Submit [@mel.as 3]
- | Cancel
-[@@deriving jsConverter { newType }]
-```
-```reasonml
-[@deriving jsConverter({newType: newType})]
-type action =
- | Click
- | [@mel.as 3] Submit
- | Cancel;
-```
-
-This feature relies on [abstract types](#abstract-types) to hide the JavaScript
-runtime representation. It will generate functions with the following types:
-
-```ocaml
-val actionToJs : action -> abs_action
-
-val actionFromJs : abs_action -> action
-```
-```reasonml
-external actionToJs: action => abs_action = ;
-
-external actionFromJs: abs_action => action = ;
-```
-
-In the case of `actionFromJs`, the return value, unlike the previous case, is
-not an option type. This is an example of "correct by construction": the only
-way to create an `abs_action` is by calling the `actionToJs` function.
-
-### Polymorphic variants
-
-The `@deriving jsConverter` attribute is applicable to polymorphic variants as
-well.
-
-> **_NOTE:_** Similarly to variants, the `@deriving jsConverter` attribute
-> cannot be used when the polymorphic variant tags have payloads. Refer to the
-> [section on runtime representation](#data-types-and-runtime-representation) to
-> learn more about how polymorphic variants are represented in JavaScript.
-
-Let’s see an example:
-
-```ocaml
-type action =
- [ `Click
- | `Submit [@mel.as "submit"]
- | `Cancel
- ]
-[@@deriving jsConverter]
-```
-```reasonml
-[@deriving jsConverter]
-type action = [
- | `Click
- | [@mel.as "submit"] `Submit
- | `Cancel
-];
-```
-
-Akin to the variant example, the following two functions will be generated:
-
-```ocaml
-val actionToJs : action -> string
-
-val actionFromJs : string -> action option
-```
-```reasonml
-external actionToJs: action => string = ;
-
-external actionFromJs: string => option(action) = ;
-```
-
-The `jsConverter { newType }` payload can also be used with polymorphic
-variants.
-
-### Records
-
-#### Accessing fields
-
-Use `@deriving accessors` on a record type to create accessor functions for its
-record field names.
-
-```ocaml
-type pet = { name : string } [@@deriving accessors]
-
-let pets = [| { name = "Brutus" }; { name = "Mochi" } |]
-
-let () = pets |. Belt.Array.map name |. Js.Array.join ~sep:"&" |. Js.log
-```
-```reasonml
-[@deriving accessors]
-type pet = {name: string};
-
-let pets = [|{name: "Brutus"}, {name: "Mochi"}|];
-
-let () = pets->(Belt.Array.map(name))->(Js.Array.join(~sep="&"))->Js.log;
-```
-
-Melange will generate a function for each field defined in the record. In this
-case, a function `name` that allows to get that field from any record of type
-`pet`:
-
-
-```ocaml
-let name (param : pet) = param.name
-```
-```reasonml
-let name = (param: pet) => param.name;
-```
-
-Considering all the above, the produced JavaScript will be:
-
-```js
-function name(param) {
- return param.name;
-}
-
-var pets = [
- {
- name: "Brutus"
- },
- {
- name: "Mochi"
- }
-];
-
-console.log(Belt_Array.map(pets, name).join("&"));
-```
-
-#### Generate JavaScript objects with optional properties
-
-In some occasions, we might want to emit a JavaScript object where some of the
-keys can be conditionally present or absent.
-
-For instance, consider the following record:
-
-```ocaml
-type person = {
- name : string;
- age : int option;
-}
-```
-```reasonml
-type person = {
- name: string,
- age: option(int),
-};
-```
-
-An example of this use-case would be expecting `{ name = "John"; age = None }`
-to generate a JavaScript object such as `{name: "Carl"}`, where the `age` key
-doesn’t appear.
-
-The `@deriving jsProperties` attribute exists to solve this problem. When
-present in a record type, `@deriving jsProperties` generates a constructor
-function for creating values of the type, where the fields marked with
-`[@mel.optional]` will be fully removed from the generated JavaScript object
-when their value is `None`.
-
-Let’s see an example. Considering this Melange code:
-
-```ocaml
-type person = {
- name : string;
- age : int option; [@mel.optional]
-}
-[@@deriving jsProperties]
-```
-```reasonml
-[@deriving jsProperties]
-type person = {
- name: string,
- [@mel.optional]
- age: option(int),
-};
-```
-
-Melange will generate a constructor to create values of this type. In our
-example, the OCaml signature would look like this after preprocessing:
-
-```ocaml
-type person
-
-val person : name:string -> ?age:int -> unit -> person
-```
-```reasonml
-type person;
-
-external person: (~name: string, ~age: int=?, unit) => person = ;
-```
-
-The `person` function can be used to create values of `person`. It is the only
-possible way to create values of this type, since Melange makes it abstract.
-Using literals like `{ name = "Alice"; age = None }` directly doesn’t type
-check.
-
-Here is an example of how we can use it:
-
-
-```ocaml
-let alice = person ~name:"Alice" ~age:20 ()
-let bob = person ~name:"Bob" ()
-```
-```reasonml
-let alice = person(~name="Alice", ~age=20, ());
-let bob = person(~name="Bob", ());
-```
-
-This will generate the following JavaScript code. Note how there is no
-JavaScript runtime overhead:
-
-```js
-var alice = {
- name: "Alice",
- age: 20
-};
-
-var bob = {
- name: "Bob"
-};
-```
-
-The `person` function uses labeled arguments to represent record fields. Because
-there is an optional argument `age`, it takes a last argument of type `unit`.
-This non-labeled argument allows to omit the optional argument on function
-application. Further details about optional labeled arguments can be found in
-[the corresponding section of the OCaml
-manual](https://v2.ocaml.org/manual/lablexamples.html#s:optional-arguments).
-
-#### Generating getters and setters
-
-In case we need both getters and setters for a record, we can use `deriving
-getSet` to have them generated for free.
-
-If we take a record like this:
-
-```ocaml
-type person = {
- name : string;
- age : int option; [@mel.optional]
-}
-[@@deriving getSet]
-```
-```reasonml
-[@deriving getSet]
-type person = {
- name: string,
- [@mel.optional]
- age: option(int),
-};
-```
-
-The `deriving` attribute can combine multiple derivers, for example we can
-combine `jsProperties` with `getSet`:
-
-```ocaml
-type person = {
- name : string;
- age : int option; [@mel.optional]
-}
-[@@deriving jsProperties, getSet]
-```
-```reasonml
-[@deriving (jsProperties, getSet)]
-type person = {
- name: string,
- [@mel.optional]
- age: option(int),
-};
-```
-
-When using `getSet`, Melange will create functions `nameGet` and `ageGet`, as
-accessors for each record field.
-
-
-```ocaml
-let twenty = ageGet alice
-
-let bob = nameGet bob
-```
-```reasonml
-let twenty = ageGet(alice);
-
-let bob = nameGet(bob);
-```
-
-This generates:
-
-```javascript
-var twenty = alice.age;
-
-var bob = bob.name;
-```
-
-The functions are named by appending `Get` to the field names of the record to
-prevent potential clashes with other values within the module. If shorter names
-are preferred for the getter functions, there is an alternate getSet { light }
getSet({light: light})
payload that can be passed
-to `deriving`:
-
-```ocaml
-type person = {
- name : string;
- age : int;
-}
-[@@deriving jsProperties, getSet { light }]
-
-let alice = person ~name:"Alice" ~age:20
-let aliceName = name alice
-```
-```reasonml
-[@deriving (jsProperties, getSet({light: light}))]
-type person = {
- name: string,
- age: int,
-};
-
-let alice = person(~name="Alice", ~age=20);
-let aliceName = name(alice);
-```
-
-Which generates:
-
-```javascript
-var alice = {
- name: "Alice",
- age: 20
-};
-
-var aliceName = alice.name;
-```
-
-In this example, the getter functions share the same names as the object fields.
-Another distinction from the previous example is that the `person` constructor
-function no longer requires the final `unit` argument since we have excluded the
-optional field in this case.
-
-> **_NOTE:_** The `mel.as` attribute can still be applied to record fields when
-> the record type is annotated with `deriving`, allowing for the renaming of
-> fields in the resulting JavaScript objects, as demonstrated in the section
-> about [binding to objects with static
-> shape](#objects-with-static-shape-record-like). However, the option to pass
-> indices to the `mel.as` decorator (like `[@mel.as "0"]`) to change the runtime
-> representation to an array is not available when using `deriving`.
-
-##### Compatibility with OCaml features
-
-The `@deriving getSet` attribute and its lightweight variant can be used with
-[mutable
-fields](https://v2.ocaml.org/manual/coreexamples.html#s:imperative-features) and
-[private types](https://v2.ocaml.org/manual/privatetypes.html), which are
-features inherited by Melange from OCaml.
-
-When the record type has mutable fields, Melange will generate setter functions
-for them. For example:
-
-```ocaml
-type person = {
- name : string;
- mutable age : int;
-}
-[@@deriving getSet]
-
-let alice = person ~name:"Alice" ~age:20
-
-let () = ageSet alice 21
-```
-```reasonml
-[@deriving getSet]
-type person = {
- name: string,
- mutable age: int,
-};
-
-let alice = person(~name="Alice", ~age=20);
-
-let () = ageSet(alice, 21);
-```
-
-This will generate:
-
-```javascript
-var alice = {
- name: "Alice",
- age: 20
-};
-
-alice.age = 21;
-```
-
-If the `mutable` keyword is omitted from the interface file, Melange will not
-include the setter function in the module signature, preventing other modules
-from mutating any values from the type.
-
-Private types can be used to prevent Melange from creating the constructor
-function. For example, if we define `person` type as private:
-
-```ocaml
-type person = private {
- name : string;
- age : int;
-}
-[@@deriving getSet]
-```
-```reasonml
-[@deriving getSet]
-type person =
- pri {
- name: string,
- age: int,
- };
-```
-
-The accessors `nameGet` and `ageGet` will still be generated, but not the
-constructor `person`. This is useful when binding to JavaScript objects while
-preventing any Melange code from creating values of such type.
-
-## Use Melange code from JavaScript
-
-As mentioned in the [build system
-section](build-system.md#commonjs-or-es6-modules), Melange allows to produce
-both CommonJS and ES6 modules. In both cases, using Melange-generated JavaScript
-code from any hand-written JavaScript file works as expected.
-
-The following definition:
-
-```ocaml
-let print name = "Hello" ^ name
-```
-```reasonml
-let print = name => "Hello" ++ name;
-```
-
-Will generate this JavaScript code, when using CommonJS (the default):
-
-```js
-function print(name) {
- return "Hello" + name;
-}
-
-exports.print = print;
-```
-
-When using ES6 (through the `(module_systems es6)` field in `melange.emit`) this
-code will be generated:
-
-```js
-function print(name) {
- return "Hello" + name;
-}
-
-export {
- print ,
-}
-```
-
-So one can use either `require` or `import` (depending on the module system of
-choice) to import the `print` value in a JavaScript file.
-
-### Default ES6 values
-
-One special case occur when working with JavaScript imports in ES6 modules that
-look like this:
-
-```js
-import ten from 'numbers.js';
-```
-
-This import expects `numbers.js` to have a default export, like:
-
-```js
-export default ten = 10;
-```
-
-To emulate this kind of exports from Melange, one just needs to define a
-`default` value.
-
-For example, in a file named numbers.ml
numbers.re
:
-
-```ocaml
-let default = 10
-```
-```reasonml
-let default = 10;
-```
-
-That way, Melange will set the value on the `default` export so it can be
-consumed as default import on the JavaScript side.
-
-## Bindings cookbook
-
-### Globals
-
-#### `window`: global variable
-
-```ocaml
-external window : Dom.window = "window"
-```
-```reasonml
-external window: Dom.window = "window";
-```
-
-See the [Using global functions or values](#using-global-functions-or-values)
-section for more information.
-
-#### `window?`: does global variable exist
-
-```ocaml
-let _ = match [%mel.external window] with
-| Some _ -> "window exists"
-| None -> "window does not exist"
-```
-```reasonml
-let _ =
- switch ([%mel.external window]) {
- | Some(_) => "window exists"
- | None => "window does not exist"
- };
-```
-
-See the [Detect global variables](#detect-global-variables) section for more
-information.
-
-#### `Math.PI`: variable in global module
-
-```ocaml
-external pi : float = "PI" [@@mel.scope "Math"]
-```
-```reasonml
-[@mel.scope "Math"] external pi: float = "PI";
-```
-
-See the [Binding to properties inside a module or
-global](#binding-to-properties-inside-a-module-or-global) section for more
-information.
-
-#### `console.log`: function in global module
-
-```ocaml
-external log : 'a -> unit = "log" [@@mel.scope "console"]
-```
-```reasonml
-[@mel.scope "console"] external log: 'a => unit = "log";
-```
-
-See the [Binding to properties inside a module or
-global](#binding-to-properties-inside-a-module-or-global) section for more
-information.
-
-### Modules
-
-#### `const path = require('path'); path.join('a', 'b')`: function in CommonJS/ES6 module
-
-```ocaml
-external join : string -> string -> string = "join" [@@mel.module "path"]
-let dir = join "a" "b"
-```
-```reasonml
-[@mel.module "path"] external join: (string, string) => string = "join";
-let dir = join("a", "b");
-```
-
-See the [Using functions from other JavaScript
-modules](#using-functions-from-other-javascript-modules) section for more
-information.
-
-#### `const foo = require('foo'); foo(1)`: import entire module as a value
-
-```ocaml
-external foo : int -> unit = "foo" [@@mel.module]
-let () = foo 1
-```
-```reasonml
-[@mel.module] external foo: int => unit = "foo";
-let () = foo(1);
-```
-
-See the [Using functions from other JavaScript
-modules](#using-functions-from-other-javascript-modules) section for more
-information.
-
-#### `import foo from 'foo'; foo(1)`: import ES6 module default export
-
-```ocaml
-external foo : int -> unit = "default" [@@mel.module "foo"]
-let () = foo 1
-```
-```reasonml
-[@mel.module "foo"] external foo: int => unit = "default";
-let () = foo(1);
-```
-
-See the [Using functions from other JavaScript
-modules](#using-functions-from-other-javascript-modules) section for more
-information.
-
-#### `const foo = require('foo'); foo.bar.baz()`: function scoped inside an object in a module
-
-```ocaml
-module Foo = struct
- module Bar = struct
- external baz : unit -> unit = "baz" [@@mel.module "foo"] [@@mel.scope "bar"]
- end
-end
-
-let () = Foo.Bar.baz ()
-```
-```reasonml
-module Foo = {
- module Bar = {
- [@mel.module "foo"] [@mel.scope "bar"] external baz: unit => unit = "baz";
- };
-};
-
-let () = Foo.Bar.baz();
-```
-
-It is not necessary to nest the binding inside OCaml modules, but mirroring the
-structure of the JavaScript module layout makes the binding more discoverable.
-
-See the [Binding to properties inside a module or
-global](#binding-to-properties-inside-a-module-or-global) section for more
-information.
-
-### Functions
-
-#### `const dir = path.join('a', 'b', ...)`: function with rest args
-
-```ocaml
-external join : string array -> string = "join" [@@mel.module "path"] [@@mel.variadic]
-let dir = join [| "a"; "b" |]
-```
-```reasonml
-[@mel.module "path"] [@mel.variadic]
-external join: array(string) => string = "join";
-let dir = join([|"a", "b"|]);
-```
-
-See the [Variadic function arguments](#variadic-function-arguments) section for
-more information.
-
-#### `const nums = range(start, stop, step)`: call a function with named arguments for readability
-
-```ocaml
-external range : start:int -> stop:int -> step:int -> int array = "range"
-let nums = range ~start:1 ~stop:10 ~step:2
-```
-```reasonml
-external range: (~start: int, ~stop: int, ~step: int) => array(int) = "range";
-let nums = range(~start=1, ~stop=10, ~step=2);
-```
-
-#### `foo('hello'); foo(true)`: overloaded function
-
-```ocaml
-external fooString : string -> unit = "foo"
-external fooBool : bool -> unit = "foo"
-
-let () = fooString ""
-let () = fooBool true
-```
-```reasonml
-external fooString: string => unit = "foo";
-external fooBool: bool => unit = "foo";
-
-let () = fooString("");
-let () = fooBool(true);
-```
-
-Melange allows specifying the name on the OCaml side and the name on the
-JavaScript side (in quotes) separately, so it's possible to bind multiple times
-to the same function with different names and signatures. This allows binding to
-complex JavaScript functions with polymorphic behaviour.
-
-#### `const nums = range(start, stop, [step])`: optional final argument(s)
-
-```ocaml
-external range : start:int -> stop:int -> ?step:int -> unit -> int array
- = "range"
-
-let nums = range ~start:1 ~stop:10 ()
-```
-```reasonml
-external range: (~start: int, ~stop: int, ~step: int=?, unit) => array(int) =
- "range";
-
-let nums = range(~start=1, ~stop=10, ());
-```
-
-When an OCaml function takes an optional parameter, it needs a positional
-parameter at the end of the parameter list to help the compiler understand when
-function application is finished and when the function can actually execute. This
-might seem cumbersome, but it is necessary in order to have out-of-the-box curried
-parameters, named parameters, and optional parameters available in the language.
-
-#### `mkdir('src/main', {recursive: true})`: options object argument
-
-```ocaml
-type mkdirOptions
-
-external mkdirOptions : ?recursive:bool -> unit -> mkdirOptions = "" [@@mel.obj]
-external mkdir : string -> ?options:mkdirOptions -> unit -> unit = "mkdir"
-
-let () = mkdir "src" ()
-let () = mkdir "src/main" ~options:(mkdirOptions ~recursive:true ()) ()
-```
-```reasonml
-type mkdirOptions;
-
-[@mel.obj] external mkdirOptions: (~recursive: bool=?, unit) => mkdirOptions;
-external mkdir: (string, ~options: mkdirOptions=?, unit) => unit = "mkdir";
-
-let () = mkdir("src", ());
-let () = mkdir("src/main", ~options=mkdirOptions(~recursive=true, ()), ());
-```
-
-See the [Objects with static shape (record-like): Using external
-functions](#using-external-functions) section for more information.
-
-#### `forEach(start, stop, item => console.log(item))`: model a callback
-
-```ocaml
-external forEach :
- start:int -> stop:int -> ((int -> unit)[@mel.uncurry]) -> unit = "forEach"
-
-let () = forEach ~start:1 ~stop:10 Js.log
-```
-```reasonml
-external forEach:
- (~start: int, ~stop: int, [@mel.uncurry] (int => unit)) => unit =
- "forEach";
-
-let () = forEach(~start=1, ~stop=10, Js.log);
-```
-
-When binding to functions with callbacks, you'll want to ensure that the
-callbacks are uncurried. `[@mel.uncurry]` is the recommended way of doing that.
-However, in some circumstances you may be forced to use the static uncurried
-function syntax. See the [Binding to callbacks](#binding-to-callbacks) section
-for more information.
-
-### Objects
-
-#### `const person = {id: 1, name: 'Alice'}`: create an object
-
-For quick creation of objects (e.g. prototyping), one can create a `Js.t` object
-literal directly:
-
-```ocaml
-let person = [%mel.obj { id = 1; name = "Alice" }]
-```
-```reasonml
-let person = {
- "id": 1,
- "name": "Alice",
-};
-```
-
-See the [Using `Js.t` objects](#using-jst-objects) section for more information.
-
-Alternatively, for greater type accuracy, one can create a record type and a
-value:
-
-```ocaml
-type person = { id : int; name : string }
-let person = { id = 1; name = "Alice" }
-```
-```reasonml
-type person = {
- id: int,
- name: string,
-};
-let person = {
- id: 1,
- name: "Alice",
-};
-```
-
-See the [Using OCaml records](#using-ocaml-records) section for more
-information.
-
-#### `person.name`: get a prop
-
-
-```ocaml
-let name = person##name
-```
-```reasonml
-let name = person##name;
-```
-
-Alternatively, if `person` value is of record type as mentioned in the section
-above:
-
-
-```ocaml
-let name = person.name
-```
-```reasonml
-let name = person.name;
-```
-
-#### `person.id = 0`: set a prop
-
-
-```ocaml
-external set_id : person -> int -> unit = "id" [@@mel.set]
-
-let () = set_id person 0
-```
-```reasonml
-[@mel.set] external set_id: (person, int) => unit = "id";
-
-let () = set_id(person, 0);
-```
-
-#### `const {id, name} = person`: object with destructuring
-
-```ocaml
-type person = { id : int; name : string }
-
-let person = { id = 1; name = "Alice" }
-let { id; name } = person
-```
-```reasonml
-type person = {
- id: int,
- name: string,
-};
-
-let person = {
- id: 1,
- name: "Alice",
-};
-let {id, name} = person;
-```
-
-### Classes and OOP
-
-In Melange it is idiomatic to bind to class properties and methods as functions
-which take the instance as just a normal function argument. So e.g., instead of
-
-```javascript
-const foo = new Foo();
-foo.bar();
-```
-
-You will write:
-
-
-```ocaml
-let foo = Foo.make ()
-let () = Foo.bar foo
-```
-```reasonml
-let foo = Foo.make();
-let () = Foo.bar(foo);
-```
-
-Note that many of the techniques shown in the [Functions](#functions) section
-are applicable to the instance members shown below.
-
-#### `const foo = new Foo()`: call a class constructor
-
-```ocaml
-module Foo = struct
- type t
- external make : unit -> t = "Foo" [@@mel.new]
-end
-
-let foo = Foo.make ()
-```
-```reasonml
-module Foo = {
- type t;
- [@mel.new] external make: unit => t = "Foo";
-};
-
-let foo = Foo.make();
-```
-
-Note the abstract type `t`, which we have revisited already in [its
-corresponding](#abstract-types) section.
-
-A Melange function binding doesn't have the context that it's binding to a
-JavaScript class like `Foo`, so you will want to explicitly put it inside a
-corresponding module `Foo` to denote the class it belongs to. In other words,
-model JavaScript classes as OCaml modules.
-
-See the [JavaScript classes](#javascript-classes) section for more information.
-
-#### `const bar = foo.bar`: get an instance property
-
-```ocaml
-module Foo = struct
- type t
- external make : unit -> t = "Foo" [@@mel.new]
- external bar : t -> int = "bar" [@@mel.get]
-end
-
-let foo = Foo.make ()
-let bar = Foo.bar foo
-```
-```reasonml
-module Foo = {
- type t;
- [@mel.new] external make: unit => t = "Foo";
- [@mel.get] external bar: t => int = "bar";
-};
-
-let foo = Foo.make();
-let bar = Foo.bar(foo);
-```
-
-See the [Binding to object properties](#bind-to-object-properties) section for
-more information.
-
-#### `foo.bar = 1`: set an instance property
-
-```ocaml
-module Foo = struct
- type t
- external make : unit -> t = "Foo" [@@mel.new]
- external setBar : t -> int -> unit = "bar" [@@mel.set]
-end
-
-let foo = Foo.make ()
-let () = Foo.setBar foo 1
-```
-```reasonml
-module Foo = {
- type t;
- [@mel.new] external make: unit => t = "Foo";
- [@mel.set] external setBar: (t, int) => unit = "bar";
-};
-
-let foo = Foo.make();
-let () = Foo.setBar(foo, 1);
-```
-
-#### `foo.meth()`: call a nullary instance method
-
-```ocaml
-module Foo = struct
- type t
-
- external make : unit -> t = "Foo" [@@mel.new]
- external meth : t -> unit = "meth" [@@mel.send]
-end
-
-let foo = Foo.make ()
-let () = Foo.meth foo
-```
-```reasonml
-module Foo = {
- type t;
-
- [@mel.new] external make: unit => t = "Foo";
- [@mel.send] external meth: t => unit = "meth";
-};
-
-let foo = Foo.make();
-let () = Foo.meth(foo);
-```
-
-See the [Calling an object method](#calling-an-object-method) section for more
-information.
-
-#### `const newStr = str.replace(substr, newSubstr)`: non-mutating instance method
-
-```ocaml
-external replace : substr:string -> newSubstr:string -> string = "replace"
-[@@mel.send.pipe: string]
-
-let str = "goodbye world"
-let substr = "goodbye"
-let newSubstr = "hello"
-let newStr = replace ~substr ~newSubstr str
-```
-```reasonml
-[@mel.send.pipe: string]
-external replace: (~substr: string, ~newSubstr: string) => string = "replace";
-
-let str = "goodbye world";
-let substr = "goodbye";
-let newSubstr = "hello";
-let newStr = replace(~substr, ~newSubstr, str);
-```
-
-`mel.send.pipe` injects a parameter of the given type (in this case `string`) as
-the final positional parameter of the binding. In other words, it creates the
-binding with the real signature substr:string -\>
-newSubstr:string -\> string -\> string
(~substr: string, ~newSubstr: string, string) =\>
-string
. This is handy for non-mutating functions as they traditionally
-take the instance as the final parameter.
-
-It is not strictly necessary to use named arguments in this binding, but it
-helps readability with multiple arguments, especially if some have the same
-type.
-
-Also note that it is not strictly need to use `mel.send.pipe`, one can use
-`mel.send` everywhere.
-
-See the [Calling an object method](#calling-an-object-method) section for more
-information.
-
-#### `arr.sort(compareFunction)`: mutating instance method
-
-
-```ocaml
-external sort : 'a array -> (('a -> 'a -> int)[@mel.uncurry]) -> 'a array
- = "sort"
-[@@mel.send]
-
-let _ = sort arr compare
-```
-```reasonml
-[@mel.send]
-external sort: (array('a), [@mel.uncurry] (('a, 'a) => int)) => array('a) =
- "sort";
-
-let _ = sort(arr, compare);
-```
-
-For a mutating method, it's traditional to pass the instance argument first.
-
-Note: `compare` is a function provided by the standard library, which fits the
-defined interface of JavaScript's comparator function.
-
-### Null and undefined
-
-#### `foo.bar === undefined`: check for undefined
+features to create *bindings* to JavaScript code. A binding is a piece of code
+that references a JavaScript object or value and makes it available for use in
+Melange code. A very basic example of a binding is:
```ocaml
-module Foo = struct
- type t
- external bar : t -> int option = "bar" [@@mel.get]
-end
-
-external foo : Foo.t = "foo"
-
-let _result = match Foo.bar foo with Some _ -> 1 | None -> 0
+external confirm : string -> bool = "window.confirm"
```
```reasonml
-module Foo = {
- type t;
- [@mel.get] external bar: t => option(int) = "bar";
-};
-
-external foo: Foo.t = "foo";
-
-let _result =
- switch (Foo.bar(foo)) {
- | Some(_) => 1
- | None => 0
- };
-```
-
-If you know some value may be `undefined` (but not `null`, see next section),
-and if you know its type is monomorphic (i.e. not generic), then you can model
-it directly as an `Option.t` type.
-
-See the [Non-shared data types](#non-shared-data-types) section for more
-information.
-
-#### `foo.bar == null`: check for null or undefined
-
-```ocaml
-module Foo = struct
- type t
- external bar : t -> t option = "bar" [@@mel.get] [@@mel.return nullable]
-end
-
-external foo : Foo.t = "foo"
-
-let _result = match Foo.bar foo with Some _ -> 1 | None -> 0
+external confirm: string => bool = "window.confirm";
```
-```reasonml
-module Foo = {
- type t;
- [@mel.get] [@mel.return nullable] external bar: t => option(t) = "bar";
-};
-external foo: Foo.t = "foo";
-
-let _result =
- switch (Foo.bar(foo)) {
- | Some(_) => 1
- | None => 0
- };
-```
+The above code binds JavaScript's
+[window.confirm](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)
+function to a Melange function named `confirm`. Note how the binding includes
+type annotations so that there's no ambiguity about the types of the arguments
+and return value.
-If you know the value is 'nullable' (i.e. could be `null` or `undefined`), or if
-the value could be polymorphic, then `mel.return nullable` is appropriate to
-use.
+---
-Note that this attribute requires the return type of the binding to be an
-`option` type as well.
+In this section:
-See the [Wrapping returned nullable values](#wrapping-returned-nullable-values)
-section for more information.
+- [Language concepts](./language-concepts.md): Learn about the language features
+ that make bindings possible
+- [Data types and runtime representations](./data-types-and-runtime-rep.md): See
+ how Melange types map to JavaScript runtime types
+- [Melange attributes and extension nodes](./attributes-and-extension-nodes.md):
+ A reference of all the built-in attributes and extension nodes that can be
+ used to create bindings
+- [Working with JavaScript objects and
+ values](./working-with-js-objects-and-values.md): A rundown of all the common
+ scenarios when binding to JavaScript objects and values (including functions)
+- [Advanced JavaScript interoperability](./advanced-js-interop.md): Advanced
+ topics such as code generation using `@deriving`
+- [Bindings cookbook](./bindings-cookbook.md): A collection of recipes that show
+ JavaScript snippets and the equivalent code in Melange
diff --git a/docs/data-types-and-runtime-rep.md b/docs/data-types-and-runtime-rep.md
new file mode 100644
index 000000000..7e46f6c44
--- /dev/null
+++ b/docs/data-types-and-runtime-rep.md
@@ -0,0 +1,294 @@
+# Data types and runtime representation
+
+This is how each Melange type is converted into JavaScript values:
+
+| Melange | JavaScript |
+|---------------------|---------------|
+| int | number |
+| nativeint | number |
+| int32 | number |
+| float | number |
+| string | string |
+| array | array |
+| tuple `(3, 4)` | array `[3, 4]` |
+| bool | boolean |
+| Js.Nullable.tJs.Nullable.t | `null` / `undefined` |
+| Js.Re.tJs.Re.t | [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) |
+| Option.t `None` | `undefined` |
+| Option.t Some( Some .. Some (None))
Some(Some( .. Some(None)))
| internal representation |
+| Option.t Some 2
Some(2)
| `2` |
+| record {x = 1; y = 2}
{x: 1; y: 2}
| object `{x: 1, y: 2}` |
+| int64 | array of 2 elements `[high, low]` high is signed, low unsigned |
+| char | `'a'` -\> `97` (ascii code) |
+| bytes | number array |
+| list `[]` | `0` |
+| list \[ x; y \]
\[x, y\]
| `{ hd: x, tl: { hd: y, tl: 0 } }` |
+| variant | See below |
+| polymorphic variant | See below |
+
+Variants with a single non-nullary constructor:
+
+```ocaml
+type tree = Leaf | Node of int * tree * tree
+(* Leaf -> 0 *)
+(* Node(7, Leaf, Leaf) -> { _0: 7, _1: 0, _2: 0 } *)
+```
+```reasonml
+type tree =
+ | Leaf
+ | Node(int, tree, tree);
+/* Leaf -> 0 */
+/* Node(7, Leaf, Leaf) -> { _0: 7, _1: 0, _2: 0 } */
+```
+
+Variants with more than one non-nullary constructor:
+
+```ocaml
+type t = A of string | B of int
+(* A("foo") -> { TAG: 0, _0: "Foo" } *)
+(* B(2) -> { TAG: 1, _0: 2 } *)
+```
+```reasonml
+type t =
+ | A(string)
+ | B(int);
+/* A("foo") -> { TAG: 0, _0: "Foo" } */
+/* B(2) -> { TAG: 1, _0: 2 } */
+```
+
+Polymorphic variants:
+
+```ocaml
+let u = `Foo (* "Foo" *)
+let v = `Foo(2) (* { NAME: "Foo", VAL: "2" } *)
+```
+```reasonml
+let u = `Foo; /* "Foo" */
+let v = `Foo(2); /* { NAME: "Foo", VAL: "2" } */
+```
+
+Let’s see now some of these types in detail. We will first describe the [shared
+types](#shared-types), which have a transparent representation as JavaScript
+values, and then go through the [non-shared types](#non-shared-data-types), that
+have more complex runtime representations.
+
+> **_NOTE:_** Relying on the non-shared data types runtime representations by
+> reading or writing them manually from JavaScript code that communicates with
+> Melange code might lead to runtime errors, as these representations might
+> change in the future.
+
+## Shared types
+
+The following are types that can be shared between Melange and JavaScript almost
+"as is". Specific caveats are mentioned on the sections where they apply.
+
+### Strings
+
+JavaScript strings are immutable sequences of UTF-16 encoded Unicode text. OCaml
+strings are immutable sequences of bytes and nowadays assumed to be UTF-8
+encoded text when interpreted as textual content. This is problematic when
+interacting with JavaScript code, because if one tries to use some unicode
+characters, like:
+
+```ocaml
+let () = Js.log "你好"
+```
+```reasonml
+let () = Js.log("你好");
+```
+
+It will lead to some cryptic console output. To rectify this, Melange allows to
+define [quoted string
+literals](https://v2.ocaml.org/manual/lex.html#sss:stringliterals) using the
+`js` identifier, for example:
+
+```ocaml
+let () = Js.log {js|你好,
+世界|js}
+```
+```reasonml
+let () = Js.log({js|你好,
+世界|js});
+```
+
+For convenience, Melange exposes another special quoted string identifier: `j`.
+It is similar to JavaScript’ string interpolation, but for variables only (not
+arbitrary expressions):
+
+```ocaml
+let world = {j|世界|j}
+let helloWorld = {j|你好,$world|j}
+```
+```reasonml
+let world = {j|世界|j};
+let helloWorld = {j|你好,$world|j};
+```
+
+You can surround the interpolation variable in parentheses too: `{j|你
+好,$(world)|j}`.
+
+To work with strings, the Melange standard library provides some utilities in
+the Stdlib.String
moduleStdlib.String
module. The
+bindings to the native JavaScript functions to work with strings are in the Js.String
moduleJs.String
module.
+
+### Floating-point numbers
+
+OCaml floats are [IEEE
+754](https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64)
+with a 53-bit mantissa and exponents from -1022 to 1023. This happens to be the
+same encoding as [JavaScript
+numbers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding),
+so values of these types can be used transparently between Melange code and
+JavaScript code. The Melange standard library provides a Stdlib.Float
+moduleStdlib.Float
module. The
+bindings to the JavaScript APIs that manipulate float values can be found in the
+Js.Float
moduleJs.Float
module.
+
+### Integers
+
+In Melange, integers are limited to 32 bits due to the [fixed-width
+conversion](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#fixed-width_number_conversion)
+of bitwise operations in JavaScript. While Melange integers compile to
+JavaScript numbers, treating them interchangeably can result in unexpected
+behavior due to differences in precision. Even though bitwise operations in
+JavaScript are constrained to 32 bits, integers themselves are represented using
+the same floating-point format [as
+numbers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding),
+allowing for a larger range of representable integers in JavaScript compared to
+Melange. When dealing with large numbers, it is advisable to use floats instead.
+For instance, floats are used in bindings like `Js.Date`.
+
+The Melange standard library provides a Stdlib.Int
moduleStdlib.Int
module. The
+bindings to work with JavaScript integers are in the Js.Int
moduleJs.Int
module.
+
+### Arrays
+
+Melange arrays compile to JavaScript arrays. But note that unlike JavaScript
+arrays, all the values in a Melange array need to have the same type.
+
+Another difference is that OCaml arrays are fixed-sized, but on Melange side
+this constraint is relaxed. You can change an array’s length using functions
+like `Js.Array.push`, available in the bindings to the JavaScript APIs in the Js.Array
moduleJs.Array
module.
+
+Melange standard library also has a module to work with arrays, available in the
+Stdlib.Array
moduleStdlib.Array
module.
+
+### Tuples
+
+OCaml tuples are compiled to JavaScript arrays. This is convenient when writing
+bindings that will use a JavaScript array with heterogeneous values, but that
+happens to have a fixed length.
+
+As a real world example of this can be found in
+[ReasonReact](https://github.com/reasonml/reason-react/), the Melange bindings
+for [React](https://react.dev/). In these bindings, component effects
+dependencies are represented as OCaml tuples, so they get compiled cleanly to
+JavaScript arrays by Melange.
+
+For example, some code like this:
+
+```ocaml
+let () = React.useEffect2 (fun () -> None) (foo, bar)
+```
+```reasonml
+let () = React.useEffect2(() => None, (foo, bar));
+```
+
+Will produce:
+
+```javascript
+React.useEffect(function () {}, [foo, bar]);
+```
+
+### Booleans
+
+Values of type `bool` compile to JavaScript booleans.
+
+### Records
+
+Melange records map directly to JavaScript objects. If the record fields include
+non-shared data types (like variants), these values should be transformed
+separately, and not be directly used in JavaScript.
+
+Extensive documentation about interfacing with JavaScript objects using records
+can be found in [the section
+below](./working-with-js-objects-and-values.md#bind-to-js-object).
+
+### Regular expressions
+
+Regular expressions created using the `%mel.re` extension node compile to their
+JavaScript counterpart.
+
+For example:
+
+```ocaml
+let r = [%mel.re "/b/g"]
+```
+```reasonml
+let r = [%mel.re "/b/g"];
+```
+
+Will compile to:
+
+```js
+var r = /b/g;
+```
+
+A regular expression like the above is of type `Js.Re.t`. The Js.Re
moduleJs.Re
module provides the
+bindings to the JavaScript functions that operate over regular expressions.
+
+## Non-shared data types
+
+The following types differ too much between Melange and JavaScript, so while
+they can always be manipulated from JavaScript, it is recommended to transform
+them before doing so.
+
+- Variants and polymorphic variants: Better transform them into readable
+ JavaScript values before manipulating them from JavaScript, Melange provides
+ [some
+ helpers](./advanced-js-interop.md#generate-getters-setters-and-constructors)
+ to do so.
+- Exceptions
+- Option (a variant type): Better use the `Js.Nullable.fromOption` and
+ `Js.Nullable.toOption` functions in the Js.Nullable
moduleJs.Nullable
module to
+ transform them into either `null` or `undefined` values.
+- List (also a variant type): use `Array.of_list` and `Array.to_list` in the Stdlib.Array
moduleStdlib.Array
module.
+- Character
+- Int64
+- Lazy values
diff --git a/docs/how-to-guides.md b/docs/how-to-guides.md
index 83e41f221..a2483b93c 100644
--- a/docs/how-to-guides.md
+++ b/docs/how-to-guides.md
@@ -537,14 +537,15 @@ v3, but node extensions (`%bs.*`) are not.
#### `@bs` attribute becomes `@u`
The `@bs` attribute, used for uncurried application (see the ["Binding to
-callbacks" section](./communicate-with-javascript.md#binding-to-callbacks)),
-becomes `@u`.
+callbacks"
+section](./working-with-js-objects-and-values.md#binding-to-callbacks)), becomes
+`@u`.
#### `@bs.val` is gone
The `@bs.val` attribute is no longer necessary, and can be removed from
`external` definitions. See more information in the ["Using global functions or
-values"](./communicate-with-javascript.md#using-global-functions-or-values)
+values"](./working-with-js-objects-and-values.md#using-global-functions-or-values)
section.
#### `Dom` and `Node` are in their own libraries
diff --git a/docs/language-concepts.md b/docs/language-concepts.md
new file mode 100644
index 000000000..481e9af34
--- /dev/null
+++ b/docs/language-concepts.md
@@ -0,0 +1,453 @@
+# Language concepts
+
+The concepts covered in the following sections are a small subset of the OCaml
+language. However, they are essential for understanding how to communicate with
+JavaScript, and the features that Melange exposes to do so.
+
+## Extension nodes and attributes
+
+In order to interact with JavaScript, Melange needs to extend the language to
+provide blocks that express these interactions.
+
+One approach could be to introduce new syntactic constructs (keywords and such)
+to do so, for example:
+
+```text
+javascript add : int -> int -> int = {|function(x,y){
+ return x + y
+}|}
+```
+
+But this would break compatibility with OCaml, which is one of the main goals of
+Melange.
+
+Fortunately, OCaml provides mechanisms to extend its language without breaking
+compatibility with the parser or the language. These mechanisms are composed by
+two parts:
+
+- First, some syntax additions to define parts of the code that will be extended
+ or replaced
+- Second, compile-time OCaml native programs called [PPX
+ rewriters](https://ocaml.org/docs/metaprogramming), that will read the syntax
+ additions defined above and proceed to extend or replace them
+
+The syntax additions come in two flavors, called [extension
+nodes](https://v2.ocaml.org/manual/extensionnodes.html) and
+[attributes](https://v2.ocaml.org/manual/attributes.html).
+
+### Extension nodes
+
+Extension nodes are blocks that are supposed to be replaced by a specific type
+of PPX rewriters called extenders. Extension nodes use the `%` character to be
+identified. Extenders will take the extension node and replace it with a valid
+OCaml AST (abstract syntax tree).
+
+An example where Melange uses extension nodes to communicate with JavaScript is
+to produce "raw" JavaScript inside a Melange program:
+
+```ocaml
+[%%mel.raw "var a = 1; var b = 2"]
+let add = [%mel.raw "a + b"]
+```
+```reasonml
+[%%mel.raw "var a = 1; var b = 2"];
+let add = [%mel.raw "a + b"];
+```
+
+Which will generate the following JavaScript code:
+
+```js
+var a = 1; var b = 2
+var add = a + b
+```
+
+The difference between one and two percentage characters is detailed in the
+[OCaml documentation](https://v2.ocaml.org/manual/extensionnodes.html).
+
+### Attributes
+
+Attributes are "decorations" applied to specific parts of the code to provide
+additional information. In Melange, attributes are used in two ways to enhance
+the expressiveness of generating JavaScript code: either reusing existing OCaml
+built-in attributes or defining new ones.
+
+#### Reusing OCaml attributes
+
+The first approach is leveraging the existing [OCaml’s built-in
+attributes](https://v2.ocaml.org/manual/attributes.html#ss:builtin-attributes)
+to be used for JavaScript generation.
+
+One prominent example of OCaml attributes that can be used in Melange projects
+is the `unboxed` attribute, which optimizes the compilation of single-field
+records and variants with a single tag to their raw values. This is useful when
+defining type aliases that we don’t want to mix up, or when binding to
+JavaScript code that uses heterogeneous collections. An example of the latter is
+discussed in the [variadic function
+arguments](./working-with-js-objects-and-values.md#variadic-function-arguments)
+section.
+
+For instance:
+
+```ocaml
+type name =
+ | Name of string [@@unboxed]
+let student_name = Name "alice"
+```
+```reasonml
+[@unboxed]
+type name =
+ | Name(string);
+let student_name = Name("alice");
+```
+
+Compiles into:
+
+```js
+var student_name = "alice";
+```
+
+Other OCaml pre-built attributes like `alert` or `deprecated` can be used with
+Melange as well.
+
+#### Defining new attributes
+
+The second approach is introducing new attributes specifically designed for
+Melange, such as the [`mel.set`
+attribute](./working-with-js-objects-and-values.md#bind-to-object-properties)
+used to bind to properties of JavaScript objects. The complete list of
+attributes introduced by Melange can be found
+[here](./attributes-and-extension-nodes.md).
+
+Attribute annotations can use one, two or three `@` characters depending on
+their placement in the code and which kind of syntax tree node they are
+annotating. More information about attributes can be found in the [dedicated
+OCaml manual page](https://v2.ocaml.org/manual/attributes.html).
+
+Here are some samples using Melange attributes
+[`mel.set`](./working-with-js-objects-and-values.md#bind-to-object-properties)
+and [`mel.as`](./working-with-js-objects-and-values.md#using-ocaml-records):
+
+```ocaml
+type document
+external setTitleDom : document -> string -> unit = "title" [@@mel.set]
+
+type t = {
+ age : int; [@mel.as "a"]
+ name : string; [@mel.as "n"]
+}
+```
+```reasonml
+type document;
+[@mel.set] external setTitleDom: (document, string) => unit = "title";
+
+type t = {
+ [@mel.as "a"]
+ age: int,
+ [@mel.as "n"]
+ name: string,
+};
+```
+
+To learn more about preprocessors, attributes and extension nodes, check the
+[section about PPX
+rewriters](https://ocaml.org/docs/metaprogramming#ppx-rewriters) in the OCaml
+docs.
+
+## External functions
+
+Most of the system that Melange exposes to communicate with JavaScript is built
+on top of an OCaml language construct called `external`.
+
+`external` is a keyword for declaring a value in OCaml that will [interface with
+C code](https://v2.ocaml.org/manual/intfc.html):
+
+```ocaml
+external my_c_function : int -> string = "someCFunctionName"
+```
+```reasonml
+external my_c_function: int => string = "someCFunctionName";
+```
+
+It is like a `let` binding, except that the body of an external is a string.
+That string has a specific meaning depending on the context. For native OCaml,
+it usually refers to a C function with that name. For Melange, it refers to the
+functions or values that exist in the runtime JavaScript code, and will be used
+from Melange.
+
+In Melange, externals can be used to [bind to global JavaScript
+objects](./working-with-js-objects-and-values.md#using-global-functions-or-values).
+They can also be decorated with certain `[@mel.xxx]` attributes to facilitate
+the creation of bindings in specific scenarios. Each one of the [available
+attributes](./attributes-and-extension-nodes.md#attributes) will be further
+explained in the next sections.
+
+Once declared, one can use an `external` as a normal value. Melange external
+functions are turned into the expected JavaScript values, inlined into their
+callers during compilation, and completely erased afterwards. They don’t appear
+in the JavaScript output, so there are no costs on bundling size.
+
+**Note**: it is recommended to use external functions and the `[@mel.xxx]`
+attributes in the interface files as well, as this allows some optimizations
+where the resulting JavaScript values can be directly inlined at the call sites.
+
+### Special identity external
+
+One external worth mentioning is the following one:
+
+```ocaml
+type foo = string
+type bar = int
+external danger_zone : foo -> bar = "%identity"
+```
+```reasonml
+type foo = string;
+type bar = int;
+external danger_zone: foo => bar = "%identity";
+```
+
+This is a final escape hatch which does nothing but convert from the type `foo`
+to `bar`. In the following sections, if you ever fail to write an `external`,
+you can fall back to using this one. But try not to.
+
+## Abstract types
+
+In the subsequent sections, you will come across examples of bindings where a
+type is defined without being assigned to a value. Here is an example:
+
+```ocaml
+type document
+```
+```reasonml
+type document;
+```
+
+These types are referred to as "abstract types" and are commonly used together
+with external functions that define operations over values when communicating
+with JavaScript.
+
+Abstract types enable defining types for specific values originating from
+JavaScript while omitting unnecessary details. An illustration is the `document`
+type mentioned earlier, which has several
+[properties](https://developer.mozilla.org/en-US/docs/Web/API/Document). By
+using abstract types, one can focus solely on the required aspects of the
+`document` value that the Melange program requires, rather than defining all its
+properties. Consider the following example:
+
+```ocaml
+type document
+
+external document : document = "document"
+external set_title : document -> string -> unit = "title" [@@mel.set]
+```
+```reasonml
+type document;
+
+external document: document = "document";
+[@mel.set] external set_title: (document, string) => unit = "title";
+```
+
+Subsequent sections delve into the details about the
+[`mel.set`](./working-with-js-objects-and-values.md#bind-to-object-properties)
+attribute and [how to bind to global
+values](./working-with-js-objects-and-values.md#using-global-functions-or-values)
+like `document`.
+
+For a comprehensive understanding of abstract types and their usefulness, refer
+to the "Encapsulation" section of the [OCaml Cornell
+textbook](https://cs3110.github.io/textbook/chapters/modules/encapsulation.html).
+
+## Pipe operators
+
+There are two pipe operators available in Melange:
+
+- A _pipe last_ operator `|>`, available [in
+ OCaml](https://v2.ocaml.org/api/Stdlib.html#1_Compositionoperators) and
+ inherited in Melange
+- A _pipe first_ operator \|.
\-\>
, available exclusively in Melange
+
+Let’s see the differences between the two.
+
+### Pipe last
+
+Since version 4.01, OCaml includes a reverse application or "pipe" (`|>`)
+operator, an infix operator that applies the result from the previous expression
+the next function. As a backend for OCaml, Melange inherits this operator.
+
+The pipe operator could be implemented like this (the real implementation is a
+bit
+[different](https://github.com/ocaml/ocaml/blob/d9547617e8b14119beacafaa2546cbebfac1bfe5/stdlib/stdlib.ml#L48)):
+
+```ocaml
+let ( |> ) f g = g f
+```
+```reasonml
+let (|>) = (f, g) => g(f);
+```
+
+This operator is useful when multiple functions are applied to some value in
+sequence, with the output of each function becoming the input of the next (a
+_pipeline_).
+
+For example, assuming we have a function `square` defined as:
+
+```ocaml
+let square x = x * x
+```
+```reasonml
+let square = x => x * x;
+```
+
+We are using it like:
+
+```ocaml
+let ten = succ (square 3)
+```
+```reasonml
+let ten = succ(square(3));
+```
+
+The pipe operator allows to write the computation for `ten` in left-to-right
+order, as [it has left
+associativity](https://v2.ocaml.org/manual/expr.html#ss:precedence-and-associativity):
+
+```ocaml
+let ten = 3 |> square |> succ
+```
+```reasonml
+let ten = 3 |> square |> succ;
+```
+
+When working with functions that can take multiple arguments, the pipe operator
+works best when the functions take the data we are processing as the last
+argument. For example:
+
+```ocaml
+let sum = List.fold_left ( + ) 0
+
+let sum_sq =
+ [ 1; 2; 3 ]
+ |> List.map square (* [1; 4; 9] *)
+ |> sum (* 1 + 4 + 9 *)
+```
+```reasonml
+let sum = List.fold_left((+), 0);
+
+let sum_sq =
+ [1, 2, 3]
+ |> List.map(square) /* [1; 4; 9] */
+ |> sum; /* 1 + 4 + 9 */
+```
+
+The above example can be written concisely because the `List.map` function in
+the [OCaml standard library](https://v2.ocaml.org/api/Stdlib.List.html) takes
+the list as the second argument. This convention is sometimes referred to as
+"data-last", and is widely adopted in the OCaml ecosystem. Data-last and the
+pipe operator `|>` work great with currying, so they are a great fit for the
+language.
+
+However, there are some limitations when using data-last when it comes to error
+handling. In the given example, if we mistakenly used the wrong function:
+
+```ocaml
+let sum_sq =
+ [ 1; 2; 3 ]
+ |> List.map String.cat
+ |> sum
+```
+```reasonml
+let sum_sq = [1, 2, 3] |> List.map(String.cat) |> sum;
+```
+
+The compiler would rightfully raise an error:
+
+4 | [ 1; 2; 3 ]
+ ^
+ Error: This expression has type int but an expression was expected of type
+ string
+ 1 | [ 1, 2, 3 ]
+ ^
+ Error: This expression has type int but an expression was expected of type
+ string
+\|.
\-\>
.
+
+### Pipe first
+
+To overcome the constraints mentioned above, Melange introduces the pipe first
+operator \|.
\-\>
.
+
+As its name suggests, the pipe first operator is better suited for functions
+where the data is passed as the first argument.
+
+The functions in the Belt
libraryBelt
library included with Melange
+have been designed with the data-first convention in mind, so they work best
+with the pipe first operator.
+
+For example, we can rewrite the example above using `Belt.List.map` and the pipe
+first operator:
+
+```ocaml
+let sum_sq =
+ [ 1; 2; 3 ]
+ |. Belt.List.map square
+ |. sum
+```
+```reasonml
+let sum_sq = [1, 2, 3]->(Belt.List.map(square))->sum;
+```
+
+We can see the difference on the error we get if the wrong function is passed to
+`Belt.List.map`:
+
+```ocaml
+let sum_sq =
+ [ 1; 2; 3 ]
+ |. Belt.List.map String.cat
+ |. sum
+```
+```reasonml
+let sum_sq = [1, 2, 3]->(Belt.List.map(String.cat))->sum;
+```
+
+The compiler will show this error message:
+
+4 | |. Belt.List.map String.cat
+ ^^^^^^^^^^
+Error: This expression has type string -> string -> string
+ but an expression was expected of type int -> 'a
+ Type string is not compatible with type int
+2 | let sum_sq = [1, 2, 3]->(Belt.List.map(String.cat))->sum;
+ ^^^^^^^^^^
+Error: This expression has type string -> string -> string
+ but an expression was expected of type int -> 'a
+ Type string is not compatible with type int
+'a
+Js.t
Js.t('a)
, where `'a` is an [OCaml
+object](https://v2.ocaml.org/manual/objectexamples.html).
+
+The advantage of objects versus records is that no type declaration is needed in
+advance, which can be helpful for prototyping or quickly generating JavaScript
+object literals.
+
+Melange provides some ways to create `Js.t` object values, as well as accessing
+the properties inside them. To create values, the `[%mel.obj]` extension is
+used, and the `##` infix operator allows to read from the object properties:
+
+```ocaml
+let john = [%mel.obj { name = "john"; age = 99 }]
+let t = john##name
+```
+```reasonml
+let john = {"name": "john", "age": 99};
+let t = john##name;
+```
+
+Which generates:
+
+```js
+var john = {
+ name: "john",
+ age: 99
+};
+
+var t = john.name;
+```
+
+Note that object types allow for some flexibility that the record types do not
+have. For example, an object type can be coerced to another with fewer values or
+methods, while it is impossible to coerce a record type to another one with
+fewer fields. So different object types that share some methods can be mixed in
+a data structure where only their common methods are visible.
+
+To give an example, one can create a function that operates in all the object
+types that include a field `name` that is of type string, e.g.:
+
+```ocaml
+let name_extended obj = obj##name ^ " wayne"
+
+let one = name_extended [%mel.obj { name = "john"; age = 99 }]
+let two = name_extended [%mel.obj { name = "jane"; address = "1 infinite loop" }]
+```
+```reasonml
+let name_extended = obj => obj##name ++ " wayne";
+
+let one = name_extended({"name": "john", "age": 99});
+let two = name_extended({"name": "jane", "address": "1 infinite loop"});
+```
+
+To read more about objects and polymorphism we recommend checking the [OCaml
+docs](https://ocaml.org/docs/objects) or the [OCaml
+manual](https://v2.ocaml.org/manual/objectexamples.html).
+
+#### Using external functions
+
+We have already explored one approach for creating JavaScript object literals by
+using [`Js.t` values and the `mel.obj` extension](#using-js-t-objects).
+
+Melange additionally offers the `mel.obj` attribute, which can be used in
+combination with external functions to create JavaScript objects. When these
+functions are called, they generate objects with fields corresponding to the
+labeled arguments of the function.
+
+If any of these labeled arguments are defined as optional and omitted during
+function application, the resulting JavaScript object will exclude the
+corresponding fields. This allows to create runtime objects and control whether
+optional keys are emitted at runtime.
+
+For example, assuming we need to bind to a JavaScript object like this:
+
+```js
+var homeRoute = {
+ type: "GET",
+ path: "/",
+ action: () => console.log("Home"),
+ // options: ...
+};
+```
+
+The first three fields are required and the `options` field is optional. You can
+declare a binding function like:
+
+```ocaml
+external route :
+ _type:string ->
+ path:string ->
+ action:(string list -> unit) ->
+ ?options:< .. > ->
+ unit ->
+ _ = ""
+ [@@mel.obj]
+```
+```reasonml
+[@mel.obj]
+external route:
+ (
+ ~_type: string,
+ ~path: string,
+ ~action: list(string) => unit,
+ ~options: {..}=?,
+ unit
+ ) =>
+ _;
+```
+
+Note that the empty string at the end of the function is used to make it
+syntactically valid. The value of this string is ignored by the compiler.
+
+Since there is an optional argument `options`, an additional unlabeled argument
+of type `unit` is included after it. It allows to omit the optional argument on
+function application. More information about labeled optional arguments can be
+found in the [OCaml
+manual](https://v2.ocaml.org/manual/lablexamples.html#s:optional-arguments).
+
+The return type of the function should be left unspecified using the wildcard
+type `_`. Melange will automatically infer the type of the resulting JavaScript
+object.
+
+In the route function, the `_type` argument starts with an underscore. When
+binding to JavaScript objects with fields that are reserved keywords in OCaml,
+Melange allows the use of an underscore prefix for the labeled arguments. The
+resulting JavaScript object will have the underscore removed from the field
+names. This is only required for the `mel.obj` attribute, while for other cases,
+the `mel.as` attribute can be used to rename fields.
+
+If we call the function like this:
+
+```ocaml
+let homeRoute = route ~_type:"GET" ~path:"/" ~action:(fun _ -> Js.log "Home") ()
+```
+```reasonml
+let homeRoute =
+ route(~_type="GET", ~path="/", ~action=_ => Js.log("Home"), ());
+```
+
+We get the following JavaScript, which does not include the `options` field
+since its argument wasn’t present:
+
+```javascript
+var homeRoute = {
+ type: "GET",
+ path: "/",
+ action: (function (param) {
+ console.log("Home");
+ })
+};
+```
+
+#### Bind to object properties
+
+If you need to bind only to the property of a JavaScript object, you can use
+`mel.get` and `mel.set` to access it using the dot notation `.`:
+
+```ocaml
+(* Abstract type for the `document` value *)
+type document
+
+external document : document = "document"
+
+external set_title : document -> string -> unit = "title" [@@mel.set]
+external get_title : document -> string = "title" [@@mel.get]
+
+let current = get_title document
+let () = set_title document "melange"
+```
+```reasonml
+/* Abstract type for the `document` value */
+type document;
+
+external document: document = "document";
+
+[@mel.set] external set_title: (document, string) => unit = "title";
+[@mel.get] external get_title: document => string = "title";
+
+let current = get_title(document);
+let () = set_title(document, "melange");
+```
+
+This generates:
+
+```javascript
+var current = document.title;
+document.title = "melange";
+```
+
+Alternatively, if some dynamism is required on the way the property is accessed,
+you can use `mel.get_index` and `mel.set_index` to access it using the bracket
+notation `[]`:
+
+```ocaml
+type t
+external create : int -> t = "Int32Array" [@@mel.new]
+external get : t -> int -> int = "" [@@mel.get_index]
+external set : t -> int -> int -> unit = "" [@@mel.set_index]
+
+let () =
+ let i32arr = (create 3) in
+ set i32arr 0 42;
+ Js.log (get i32arr 0)
+```
+```reasonml
+type t;
+[@mel.new] external create: int => t = "Int32Array";
+[@mel.get_index] external get: (t, int) => int;
+[@mel.set_index] external set: (t, int, int) => unit;
+
+let () = {
+ let i32arr = create(3);
+ set(i32arr, 0, 42);
+ Js.log(get(i32arr, 0));
+};
+```
+
+Which generates:
+
+```js
+var i32arr = new Int32Array(3);
+i32arr[0] = 42;
+console.log(i32arr[0]);
+```
+
+### Objects with dynamic shape (dictionary-like)
+
+Sometimes JavaScript objects are used as dictionaries. In these cases:
+
+- All values stored in the object belong to the same type
+- Key-value pairs can be added or removed at runtime
+
+For this particular use case of JavaScript objects, Melange exposes a specific
+type `Js.Dict.t`. The values and functions to work with values of this type are
+defined in the Js.Dict
moduleJs.Dict
module, with operations
+like `get`, `set`, etc.
+
+Values of the type `Js.Dict.t` compile to JavaScript objects.
+
+### JavaScript classes
+
+JavaScript classes are special kinds of objects. To interact with classes,
+Melange exposes `mel.new` to emulate e.g. `new Date()`:
+
+```ocaml
+type t
+external create_date : unit -> t = "Date" [@@mel.new]
+let date = create_date ()
+```
+```reasonml
+type t;
+[@mel.new] external create_date: unit => t = "Date";
+let date = create_date();
+```
+
+Which generates:
+
+```js
+var date = new Date();
+```
+
+You can chain `mel.new` and `mel.module` if the JavaScript class you want to
+work with is in a separate JavaScript module:
+
+```ocaml
+type t
+external book : unit -> t = "Book" [@@mel.new] [@@mel.module]
+let myBook = book ()
+```
+```reasonml
+type t;
+[@mel.new] [@mel.module] external book: unit => t = "Book";
+let myBook = book();
+```
+
+Which generates:
+
+```js
+var Book = require("Book");
+var myBook = new Book();
+```
+
+## Bind to JavaScript functions or values
+
+### Using global functions or values
+
+Binding to a JavaScript function available globally makes use of `external`,
+like with objects. But unlike objects, there is no need to add any attributes:
+
+```ocaml
+(* Abstract type for `timeoutId` *)
+type timeoutId
+external setTimeout : (unit -> unit) -> int -> timeoutId = "setTimeout"
+external clearTimeout : timeoutId -> unit = "clearTimeout"
+
+let id = setTimeout (fun () -> Js.log "hello") 100
+let () = clearTimeout id
+```
+```reasonml
+/* Abstract type for `timeoutId` */
+type timeoutId;
+external setTimeout: (unit => unit, int) => timeoutId = "setTimeout";
+external clearTimeout: timeoutId => unit = "clearTimeout";
+
+let id = setTimeout(() => Js.log("hello"), 100);
+let () = clearTimeout(id);
+```
+
+> **_NOTE:_** The bindings to `setTimeout` and `clearTimeout` are shown here for
+> learning purposes, but they are already available in the target="_self" href="./api/ml/melange/Js/Global">Js.Global
+> module href="./api/re/melange/Js/Global">Js.Global
module.
+
+Generates:
+
+```javascript
+var id = setTimeout(function (param) {
+ console.log("hello");
+}, 100);
+
+clearTimeout(id);
+```
+
+Global bindings can also be applied to values:
+
+```ocaml
+(* Abstract type for `document` *)
+type document
+
+external document : document = "document"
+let document = document
+```
+```reasonml
+/* Abstract type for `document` */
+type document;
+
+external document: document = "document";
+let document = document;
+```
+
+Which generates:
+
+```javascript
+var doc = document;
+```
+
+### Using functions from other JavaScript modules
+
+`mel.module` allows to bind to values that belong to another JavaScript module.
+It accepts a string with the name of the module, or the relative path to it.
+
+```ocaml
+external dirname : string -> string = "dirname" [@@mel.module "path"]
+let root = dirname "/User/github"
+```
+```reasonml
+[@mel.module "path"] external dirname: string => string = "dirname";
+let root = dirname("/User/github");
+```
+
+Generates:
+
+```js
+var Path = require("path");
+var root = Path.dirname("/User/github");
+```
+
+### Binding to properties inside a module or global
+
+For cases when we need to create bindings for a property within a module or a
+global JavaScript object, Melange provides the `mel.scope` attribute.
+
+For example, if we want to write some bindings for a specific property
+`commands` from [the `vscode`
+package](https://code.visualstudio.com/api/references/vscode-api#commands), we
+can do:
+
+```ocaml
+type param
+external executeCommands : string -> param array -> unit = ""
+ [@@mel.scope "commands"] [@@mel.module "vscode"] [@@mel.variadic]
+
+let f a b c = executeCommands "hi" [| a; b; c |]
+```
+```reasonml
+type param;
+[@mel.scope "commands"] [@mel.module "vscode"] [@mel.variadic]
+external executeCommands: (string, array(param)) => unit;
+
+let f = (a, b, c) => executeCommands("hi", [|a, b, c|]);
+```
+
+Which compiles to:
+
+```javascript
+var Vscode = require("vscode");
+
+function f(a, b, c) {
+ Vscode.commands.executeCommands("hi", a, b, c);
+}
+```
+
+The `mel.scope` attribute can take multiple arguments as payload, in case we
+want to reach deeper into the object from the module we are importing.
+
+For example:
+
+```ocaml
+type t
+
+external back : t = "back"
+ [@@mel.module "expo-camera"] [@@mel.scope "Camera", "Constants", "Type"]
+
+let camera_type_back = back
+```
+```reasonml
+type t;
+
+[@mel.module "expo-camera"] [@mel.scope ("Camera", "Constants", "Type")]
+external back: t = "back";
+
+let camera_type_back = back;
+```
+
+Which generates:
+
+```javascript
+var ExpoCamera = require("expo-camera");
+
+var camera_type_back = ExpoCamera.Camera.Constants.Type.back;
+```
+
+It can be used without `mel.module`, to created scoped bindings to global
+values:
+
+```ocaml
+external imul : int -> int -> int = "imul" [@@mel.scope "Math"]
+
+let res = imul 1 2
+```
+```reasonml
+[@mel.scope "Math"] external imul: (int, int) => int = "imul";
+
+let res = imul(1, 2);
+```
+
+Which produces:
+
+```javascript
+var res = Math.imul(1, 2);
+```
+
+Or it can be used together with `mel.new`:
+
+```ocaml
+type t
+
+external create : unit -> t = "GUI"
+ [@@mel.new] [@@mel.scope "default"] [@@mel.module "dat.gui"]
+
+let gui = create ()
+```
+```reasonml
+type t;
+
+[@mel.new] [@mel.scope "default"] [@mel.module "dat.gui"]
+external create: unit => t = "GUI";
+
+let gui = create();
+```
+
+Which generates:
+
+
+```javascript
+var DatGui = require("dat.gui");
+
+var gui = new (DatGui.default.GUI)();
+```
+
+### Labeled arguments
+
+OCaml has [labeled arguments](https://v2.ocaml.org/manual/lablexamples.html),
+which can also be optional, and work with `external` as well.
+
+Labeled arguments can be useful to provide more information about the arguments
+of a JavaScript function that is called from Melange.
+
+Let’s say we have the following JavaScript function, that we want to call from
+Melange:
+
+```js
+// MyGame.js
+
+function draw(x, y, border) {
+ // let’s assume `border` is optional and defaults to false
+}
+draw(10, 20)
+draw(20, 20, true)
+```
+
+When writing Melange bindings, we can add labeled arguments to make things more
+clear:
+
+```ocaml
+external draw : x:int -> y:int -> ?border:bool -> unit -> unit = "draw"
+ [@@mel.module "MyGame"]
+
+let () = draw ~x:10 ~y:20 ~border:true ()
+let () = draw ~x:10 ~y:20 ()
+```
+```reasonml
+[@mel.module "MyGame"]
+external draw: (~x: int, ~y: int, ~border: bool=?, unit) => unit = "draw";
+
+let () = draw(~x=10, ~y=20, ~border=true, ());
+let () = draw(~x=10, ~y=20, ());
+```
+
+Generates:
+
+```js
+var MyGame = require("MyGame");
+
+MyGame.draw(10, 20, true);
+MyGame.draw(10, 20, undefined);
+```
+
+The generated JavaScript function is the same, but now the usage in Melange is
+much clearer.
+
+**Note**: in this particular case, a final param of type unit, `()` must be
+added after `border`, since `border` is an optional argument at the last
+position. Not having the last param `unit` would lead to a warning, which is
+explained in detail [in the OCaml
+documentation](https://ocaml.org/docs/labels#warning-this-optional-argument-cannot-be-erased).
+
+Note that you can freely reorder the labeled arguments when applying the
+function on the Melange side. The generated code will maintain the original
+order that was used when declaring the function:
+
+```ocaml
+external draw : x:int -> y:int -> ?border:bool -> unit -> unit = "draw"
+ [@@mel.module "MyGame"]
+let () = draw ~x:10 ~y:20 ()
+let () = draw ~y:20 ~x:10 ()
+```
+```reasonml
+[@mel.module "MyGame"]
+external draw: (~x: int, ~y: int, ~border: bool=?, unit) => unit = "draw";
+let () = draw(~x=10, ~y=20, ());
+let () = draw(~y=20, ~x=10, ());
+```
+
+Generates:
+
+```js
+var MyGame = require("MyGame");
+
+MyGame.draw(10, 20, undefined);
+MyGame.draw(10, 20, undefined);
+```
+
+### Calling an object method
+
+If we need to call a JavaScript method, Melange provides the attribute
+`mel.send`.
+
+> In the following snippets, we will be referring to a type `Dom.element`, which
+> is provided within the library `melange.dom`. You can add it to your project
+> by including `(libraries melange.dom)` to your `dune` file:
+
+```ocaml
+(* Abstract type for the `document` global *)
+type document
+
+external document : document = "document"
+external get_by_id : document -> string -> Dom.element = "getElementById"
+ [@@mel.send]
+
+let el = get_by_id document "my-id"
+```
+```reasonml
+/* Abstract type for the `document` global */
+type document;
+
+external document: document = "document";
+[@mel.send]
+external get_by_id: (document, string) => Dom.element = "getElementById";
+
+let el = get_by_id(document, "my-id");
+```
+
+Generates:
+
+```js
+var el = document.getElementById("my-id");
+```
+
+When using `mel.send`, the first argument will be the object that holds the
+property with the function we want to call. This combines well with the pipe
+first operator \|.
\-\>
, see the ["Chaining"](#chaining) section
+below.
+
+If we want to design our bindings to be used with OCaml pipe last operator `|>`,
+there is an alternate `mel.send.pipe` attribute. Let’s rewrite the example above
+using it:
+
+```ocaml
+(* Abstract type for the `document` global *)
+type document
+
+external document : document = "document"
+external get_by_id : string -> Dom.element = "getElementById"
+ [@@mel.send.pipe: document]
+
+let el = get_by_id "my-id" document
+```
+```reasonml
+/* Abstract type for the `document` global */
+type document;
+
+external document: document = "document";
+[@mel.send.pipe: document]
+external get_by_id: string => Dom.element = "getElementById";
+
+let el = get_by_id("my-id", document);
+```
+
+Generates the same code as `mel.send`:
+
+```js
+var el = document.getElementById("my-id");
+```
+
+#### Chaining
+
+It is common to find this kind of API in JavaScript: `foo().bar().baz()`. This
+kind of API can be designed with Melange externals. Depending on which
+convention we want to use, there are two attributes available:
+
+- For a data-first convention, the `mel.send` attribute, in combination with
+ [the pipe first operator](./language-concepts.md#pipe-first) \|.
\-\>
+- For a data-last convention, the `mel.send.pipe` attribute, in combination with
+ OCaml [pipe last operator](./language-concepts.md#pipe-last) `|>`.
+
+Let’s see first an example of chaining using data-first convention with the pipe
+first operator \|.
\-\>
:
+
+```ocaml
+(* Abstract type for the `document` global *)
+type document
+
+external document : document = "document"
+external get_by_id : document -> string -> Dom.element = "getElementById"
+ [@@mel.send]
+external get_by_classname : Dom.element -> string -> Dom.element
+ = "getElementsByClassName"
+ [@@mel.send]
+
+let el = document |. get_by_id "my-id" |. get_by_classname "my-class"
+```
+```reasonml
+/* Abstract type for the `document` global */
+type document;
+
+external document: document = "document";
+[@mel.send]
+external get_by_id: (document, string) => Dom.element = "getElementById";
+[@mel.send]
+external get_by_classname: (Dom.element, string) => Dom.element =
+ "getElementsByClassName";
+
+let el = document->(get_by_id("my-id"))->(get_by_classname("my-class"));
+```
+
+Will generate:
+
+```javascript
+var el = document.getElementById("my-id").getElementsByClassName("my-class");
+```
+
+Now with pipe last operator `|>`:
+
+```ocaml
+(* Abstract type for the `document` global *)
+type document
+
+external document : document = "document"
+external get_by_id : string -> Dom.element = "getElementById"
+ [@@mel.send.pipe: document]
+external get_by_classname : string -> Dom.element = "getElementsByClassName"
+ [@@mel.send.pipe: Dom.element]
+
+let el = document |> get_by_id "my-id" |> get_by_classname "my-class"
+```
+```reasonml
+/* Abstract type for the `document` global */
+type document;
+
+external document: document = "document";
+[@mel.send.pipe: document]
+external get_by_id: string => Dom.element = "getElementById";
+[@mel.send.pipe: Dom.element]
+external get_by_classname: string => Dom.element = "getElementsByClassName";
+
+let el = document |> get_by_id("my-id") |> get_by_classname("my-class");
+```
+
+Will generate the same JavaScript as the pipe first version:
+
+```javascript
+var el = document.getElementById("my-id").getElementsByClassName("my-class");
+```
+
+### Variadic function arguments
+
+Sometimes JavaScript functions take an arbitrary amount of arguments. For these
+cases, Melange provides the `mel.variadic` attribute, which can be attached to
+the `external` declaration. However, there is one caveat: all the variadic
+arguments need to belong to the same type.
+
+```ocaml
+external join : string array -> string = "join"
+ [@@mel.module "path"] [@@mel.variadic]
+let v = join [| "a"; "b" |]
+```
+```reasonml
+[@mel.module "path"] [@mel.variadic]
+external join: array(string) => string = "join";
+let v = join([|"a", "b"|]);
+```
+
+Generates:
+
+```js
+var Path = require("path");
+var v = Path.join("a", "b");
+```
+
+If more dynamism is needed, there is a way to inject elements with different
+types in the array and still have Melange compile to JavaScript values that are
+not wrapped using the OCaml
+[`unboxed`](https://v2.ocaml.org/manual/attributes.html) attribute, which was
+mentioned [in the OCaml attributes
+section](/language-concepts#reusing-ocaml-attributes):
+
+```ocaml
+type hide = Hide : 'a -> hide [@@unboxed]
+
+external join : hide array -> string = "join" [@@mel.module "path"] [@@mel.variadic]
+
+let v = join [| Hide "a"; Hide 2 |]
+```
+```reasonml
+[@unboxed]
+type hide =
+ | Hide('a): hide;
+
+[@mel.module "path"] [@mel.variadic]
+external join: array(hide) => string = "join";
+
+let v = join([|Hide("a"), Hide(2)|]);
+```
+
+Compiles to:
+
+```javascript
+var Path = require("path");
+
+var v = Path.join("a", 2);
+```
+
+### Bind to a polymorphic function
+
+Some JavaScript libraries will define functions where the arguments can vary on
+both type and shape. There are two approaches to bind to those, depending on how
+dynamic they are.
+
+#### Approach 1: Multiple external functions
+
+If it is possible to enumerate the many forms an overloaded JavaScript function
+can take, a flexible approach is to bind to each form individually:
+
+```ocaml
+external drawCat : unit -> unit = "draw" [@@mel.module "MyGame"]
+external drawDog : giveName:string -> unit = "draw" [@@mel.module "MyGame"]
+external draw : string -> useRandomAnimal:bool -> unit = "draw"
+ [@@mel.module "MyGame"]
+```
+```reasonml
+[@mel.module "MyGame"] external drawCat: unit => unit = "draw";
+[@mel.module "MyGame"] external drawDog: (~giveName: string) => unit = "draw";
+[@mel.module "MyGame"]
+external draw: (string, ~useRandomAnimal: bool) => unit = "draw";
+```
+
+Note how all three externals bind to the same JavaScript function, `draw`.
+
+#### Approach 2: Polymorphic variant + `mel.unwrap`
+
+In some cases, the function has a constant number of arguments but the type of
+the argument can vary. For cases like this, we can model the argument as a
+variant and use the `mel.unwrap` attribute in the external.
+
+Let’s say we want to bind to the following JavaScript function:
+
+```js
+function padLeft(value, padding) {
+ if (typeof padding === "number") {
+ return Array(padding + 1).join(" ") + value;
+ }
+ if (typeof padding === "string") {
+ return padding + value;
+ }
+ throw new Error(`Expected string or number, got '${padding}'.`);
+}
+```
+
+As the `padding` argument can be either a number or a string, we can use
+`mel.unwrap` to define it. It is important to note that `mel.unwrap` imposes
+certain requirements on the type it is applied to:
+
+- It needs to be a [polymorphic
+ variant](https://v2.ocaml.org/manual/polyvariant.html)
+- Its definition needs to be inlined
+- Each variant tag needs to have an argument
+- The variant type can not be opened (can’t use `>`)
+
+```ocaml
+external padLeft:
+ string
+ -> ([ `Str of string
+ | `Int of int
+ ] [@mel.unwrap])
+ -> string
+ = "padLeft"
+
+let _ = padLeft "Hello World" (`Int 4)
+let _ = padLeft "Hello World" (`Str "Message from Melange: ")
+```
+```reasonml
+external padLeft:
+ (string, [@mel.unwrap] [ | `Str(string) | `Int(int)]) => string =
+ "padLeft";
+
+let _ = padLeft("Hello World", `Int(4));
+let _ = padLeft("Hello World", `Str("Message from Melange: "));
+```
+
+Which produces the following JavaScript:
+
+```js
+padLeft("Hello World", 4);
+padLeft("Hello World", "Message from Melange: ");
+```
+
+As we saw in the [Non-shared data
+types](./data-types-and-runtime-rep.md#non-shared-data-types) section, we should
+rather avoid passing variants directly to the JavaScript side. By using
+`mel.unwrap` we get the best of both worlds: from Melange we can use variants,
+while JavaScript gets the raw values inside them.
+
+### Using polymorphic variants to bind to enums
+
+Some JavaScript APIs take a limited subset of values as input. For example,
+Node’s `fs.readFileSync` second argument can only take a few given string
+values: `"ascii"`, `"utf8"`, etc. Some other functions can take values from a
+few given integers, like the `createStatusBarItem` function in VS Code API,
+which can take an `alignment` parameter that can only be [`1` or
+`2`](https://github.com/Microsoft/vscode/blob/2362ec665c84a1519162b50c36ed4f29d1e20f62/src/vs/vscode.d.ts#L4098-L4109).
+
+One could still type these parameters as just `string` or `int`, but this would
+not prevent consumers of the external function from calling it using values that
+are unsupported by the JavaScript function. Let’s see how we can use polymorphic
+variants to avoid runtime errors.
+
+If the values are strings, we can use the `mel.string` attribute:
+
+```ocaml
+external read_file_sync :
+ name:string -> ([ `utf8 | `ascii ][@mel.string]) -> string = "readFileSync"
+ [@@mel.module "fs"]
+
+let _ = read_file_sync ~name:"xx.txt" `ascii
+```
+```reasonml
+[@mel.module "fs"]
+external read_file_sync:
+ (~name: string, [@mel.string] [ | `utf8 | `ascii]) => string =
+ "readFileSync";
+
+let _ = read_file_sync(~name="xx.txt", `ascii);
+```
+
+Which generates:
+
+```js
+var Fs = require("fs");
+Fs.readFileSync("xx.txt", "ascii");
+```
+
+This technique can be combined with the `mel.as` attribute to modify the strings
+produced from the polymorphic variant values. For example:
+
+```ocaml
+type document
+type style
+
+external document : document = "document"
+external get_by_id : document -> string -> Dom.element = "getElementById"
+[@@mel.send]
+external style : Dom.element -> style = "style" [@@mel.get]
+external transition_timing_function :
+ style ->
+ ([ `ease
+ | `easeIn [@mel.as "ease-in"]
+ | `easeOut [@mel.as "ease-out"]
+ | `easeInOut [@mel.as "ease-in-out"]
+ | `linear ]
+ [@mel.string]) ->
+ unit = "transitionTimingFunction"
+[@@mel.set]
+
+let element_style = style (get_by_id document "my-id")
+let () = transition_timing_function element_style `easeIn
+```
+```reasonml
+type document;
+type style;
+
+external document: document = "document";
+[@mel.send]
+external get_by_id: (document, string) => Dom.element = "getElementById";
+[@mel.get] external style: Dom.element => style = "style";
+[@mel.set]
+external transition_timing_function:
+ (
+ style,
+ [@mel.string] [
+ | `ease
+ | [@mel.as "ease-in"] `easeIn
+ | [@mel.as "ease-out"] `easeOut
+ | [@mel.as "ease-in-out"] `easeInOut
+ | `linear
+ ]
+ ) =>
+ unit =
+ "transitionTimingFunction";
+
+let element_style = style(get_by_id(document, "my-id"));
+let () = transition_timing_function(element_style, `easeIn);
+```
+
+This will generate:
+
+```javascript
+var element_style = document.getElementById("my-id").style;
+
+element_style.transitionTimingFunction = "ease-in";
+```
+
+Aside from producing string values, Melange also offers `mel.int` to produce
+integer values. `mel.int` can also be combined with `mel.as`:
+
+```ocaml
+external test_int_type :
+ ([ `on_closed | `on_open [@mel.as 20] | `in_bin ][@mel.int]) -> int
+ = "testIntType"
+
+let value = test_int_type `on_open
+```
+```reasonml
+external test_int_type:
+ ([@mel.int] [ | `on_closed | [@mel.as 20] `on_open | `in_bin]) => int =
+ "testIntType";
+
+let value = test_int_type(`on_open);
+```
+
+In this example, `on_closed` will be encoded as 0, `on_open` will be 20 due to
+the attribute `mel.as` and `in_bin` will be 21, because if no `mel.as`
+annotation is provided for a variant tag, the compiler continues assigning
+values counting up from the previous one.
+
+This code generates:
+
+```js
+var value = testIntType(20);
+```
+
+### Using polymorphic variants to bind to event listeners
+
+Polymorphic variants can also be used to wrap event listeners, or any other kind
+of callback, for example:
+
+```ocaml
+type readline
+
+external on :
+ readline ->
+ ([ `close of unit -> unit | `line of string -> unit ][@mel.string]) ->
+ readline = "on"
+ [@@mel.send]
+
+let register rl =
+ rl |. on (`close (fun event -> ())) |. on (`line (fun line -> Js.log line))
+```
+```reasonml
+type readline;
+
+[@mel.send]
+external on:
+ (
+ readline,
+ [@mel.string] [ | `close(unit => unit) | `line(string => unit)]
+ ) =>
+ readline =
+ "on";
+
+let register = rl =>
+ rl->(on(`close(event => ())))->(on(`line(line => Js.log(line))));
+```
+
+This generates:
+
+```js
+function register(rl) {
+ return rl
+ .on("close", function($$event) {})
+ .on("line", function(line) {
+ console.log(line);
+ });
+}
+```
+
+### Constant values as arguments
+
+Sometimes we want to call a JavaScript function and make sure one of the
+arguments is always constant. For this, the `[@mel.as]` attribute can be
+combined with the wildcard pattern `_`:
+
+```ocaml
+external process_on_exit : (_[@mel.as "exit"]) -> (int -> unit) -> unit
+ = "process.on"
+
+let () =
+ process_on_exit (fun exit_code ->
+ Js.log ("error code: " ^ string_of_int exit_code))
+```
+```reasonml
+external process_on_exit: ([@mel.as "exit"] _, int => unit) => unit =
+ "process.on";
+
+let () =
+ process_on_exit(exit_code =>
+ Js.log("error code: " ++ string_of_int(exit_code))
+ );
+```
+
+This generates:
+
+```js
+process.on("exit", function (exitCode) {
+ console.log("error code: " + exitCode.toString());
+});
+```
+
+The `mel.as "exit"` and the wildcard `_` pattern together will tell Melange to
+compile the first argument of the JavaScript function to the string `"exit"`.
+
+You can also use any JSON literal by passing a quoted string to `mel.as`:
+`mel.as {json|true|json}` or `mel.as {json|{"name": "John"}|json}`.
+
+### Binding to callbacks
+
+In OCaml, all functions have arity 1. This means that if you define a function
+like this:
+
+```ocaml
+let add x y = x + y
+```
+```reasonml
+let add = (x, y) => x + y;
+```
+
+Its type will be `int -> int -> int`. This means that one can partially apply
+`add` by calling `add 1`, which will return another function expecting the
+second argument of the addition. This kind of functions are called "curried"
+functions, more information about currying in OCaml can be found in [this
+chapter](https://cs3110.github.io/textbook/chapters/hop/currying.html) of the
+"OCaml Programming: Correct + Efficient + Beautiful" book.
+
+This is incompatible with how function calling conventions work in JavaScript,
+where all function calls always apply all the arguments. To continue the
+example, let’s say we have an `add` function implemented in JavaScript, similar
+to the one above:
+
+```javascript
+var add = function (a, b) {
+ return a + b;
+};
+```
+
+If we call `add(1)`, the function will be totally applied, with `b` having
+`undefined` value. And as JavaScript will try to add `1` with `undefined`, we
+will get `NaN` as a result.
+
+To illustrate this difference and how it affects Melange bindings, let’s say we
+want to write bindings for a JavaScript function like this:
+
+```javascript
+function map (a, b, f){
+ var i = Math.min(a.length, b.length);
+ var c = new Array(i);
+ for(var j = 0; j < i; ++j){
+ c[j] = f(a[i],b[i])
+ }
+ return c ;
+}
+```
+
+A naive external function declaration could be as below:
+
+```ocaml
+external map : 'a array -> 'b array -> ('a -> 'b -> 'c) -> 'c array = "map"
+```
+```reasonml
+external map: (array('a), array('b), ('a, 'b) => 'c) => array('c) = "map";
+```
+
+Unfortunately, this is not completely correct. The issue is in the callback
+function, with type `'a -> 'b -> 'c`. This means that `map` will expect a
+function like `add` described above. But as we said, in OCaml, having two
+arguments means just to have two functions that take one argument.
+
+Let’s rewrite `add` to make the problem a bit more clear:
+
+```ocaml
+let add x = let partial y = x + y in partial
+```
+```reasonml
+let add = x => {
+ let partial = y => x + y;
+ partial;
+};
+```
+
+This will be compiled to:
+
+```javascript
+function add(x) {
+ return (function (y) {
+ return x + y | 0;
+ });
+}
+```
+
+Now if we ever used our external function `map` with our `add` function by
+calling `map arr1 arr2 add` it would not work as expected. JavaScript function
+application does not work the same as in OCaml, so the function call in the
+`map` implementation, `f(a[i],b[i])`, would be applied over the outer JavaScript
+function `add`, which only takes one argument `x`, and `b[i]` would be just
+discarded. The value returned from the operation would not be the addition of
+the two numbers, but rather the inner anonymous callback.
+
+To solve this mismatch between OCaml and JavaScript functions and their
+application, Melange provides a special attribute `@u` that can be used to
+annotate external functions that need to be "uncurried".
+
+In Reason syntax, this attribute does not need to be
+written explicitly, as it is deeply integrated with the Reason parser. To
+specify some function type as "uncurried", one just needs to add the dot
+character `.` to the function type. For example, `(. 'a, 'b) => 'c` instead of
+`('a, 'b) => 'c`.
+
+In the example above:
+
+```ocaml
+external map : 'a array -> 'b array -> (('a -> 'b -> 'c)[@u]) -> 'c array
+ = "map"
+```
+```reasonml
+external map: (array('a), array('b), (. 'a, 'b) => 'c) => array('c) = "map";
+```
+
+Here `('a -> 'b -> 'c [@u])``(. 'a, 'b) => 'c`will be interpreted as having
+arity 2. In general, `'a0 -> 'a1 ... 'aN -> 'b0 [@u]`
+is the same as `'a0 -> 'a1 ... 'aN -> 'b0``.
+'a0, 'a1, ... 'aN => 'b0` is the same as `'a0, 'a1, ... 'aN => 'b0`
+except the former’s arity is guaranteed to be N while the latter is unknown.
+
+If we try now to call `map` using `add`:
+
+```ocaml
+let add x y = x + y
+let _ = map [||] [||] add
+```
+```reasonml
+let add = (x, y) => x + y;
+let _ = map([||], [||], add);
+```
+We will get an error:
+
+```text
+let _ = map [||] [||] add
+ ^^^
+This expression has type int -> int -> int
+but an expression was expected of type ('a -> 'b -> 'c) Js.Fn.arity2
+```
+
+To solve this, we add `@u``.` in the function definition as well:
+
+```ocaml
+let add = fun [@u] x y -> x + y
+```
+```reasonml
+let add = (. x, y) => x + y;
+```
+
+Annotating function definitions can be quite cumbersome when writing a lot of
+externals.
+
+To work around the verbosity, Melange offers another attribute called
+`mel.uncurry`.
+
+Let’s see how we could use it in the previous example. We just need to replace
+`u` with `mel.uncurry`:
+
+```ocaml
+external map :
+ 'a array -> 'b array -> (('a -> 'b -> 'c)[@mel.uncurry]) -> 'c array = "map"
+```
+```reasonml
+external map:
+ (array('a), array('b), [@mel.uncurry] (('a, 'b) => 'c)) => array('c) =
+ "map";
+```
+
+Now if we try to call `map` with a regular `add` function:
+
+```ocaml
+let add x y = x + y
+let _ = map [||] [||] add
+```
+```reasonml
+let add = (x, y) => x + y;
+let _ = map([||], [||], add);
+```
+
+Everything works fine now, without having to attach any attributes to `add`.
+
+The main difference between `u` and `mel.uncurry` is that the latter only works
+with externals. `mel.uncurry` is the recommended option to use for bindings,
+while `u` remains useful for those use cases where performance is crucial and we
+want the JavaScript functions generated from OCaml ones to not be applied
+partially.
+
+### Modeling `this`\-based Callbacks
+
+Many JavaScript libraries have callbacks which rely on the [`this`
+keyword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this),
+for example:
+
+```js
+x.onload = function(v) {
+ console.log(this.response + v)
+}
+```
+
+Inside the `x.onload` callback, `this` would be pointing to `x`. It would not be
+correct to declare `x.onload` of type `unit -> unit`. Instead, Melange
+introduces a special attribute, `mel.this`, which allows to type `x` as this:
+
+```ocaml
+type x
+external x : x = "x"
+external set_onload : x -> ((x -> int -> unit)[@mel.this]) -> unit = "onload"
+ [@@mel.set]
+external resp : x -> int = "response" [@@mel.get]
+let _ =
+ set_onload x
+ begin
+ fun [@mel.this] o v -> Js.log (resp o + v)
+ end
+```
+```reasonml
+type x;
+external x: x = "x";
+[@mel.set]
+external set_onload: (x, [@mel.this] ((x, int) => unit)) => unit = "onload";
+[@mel.get] external resp: x => int = "response";
+let _ = set_onload(x, [@mel.this] (o, v) => Js.log(resp(o) + v));
+```
+
+Which generates:
+
+```javascript
+x.onload = function (v) {
+ var o = this;
+ console.log((o.response + v) | 0);
+};
+```
+
+Note that the first argument will be reserved for `this`.
+
+### Wrapping returned nullable values
+
+JavaScript models `null` and `undefined` differently, whereas it can be useful
+to treat both as `'a option``option('a)` in Melange.
+
+Melange understands the `mel.return` attribute in externals to model how
+nullable return types should be wrapped at the bindings boundary. An `external`
+value with `mel.return` converts the return value to an `option` type, avoiding
+the need for extra wrapping / unwrapping with functions such as
+`Js.Nullable.toOption`.
+
+```ocaml
+type element
+type document
+external get_by_id : document -> string -> element option = "getElementById"
+ [@@mel.send] [@@mel.return nullable]
+
+let test document =
+ let elem = get_by_id document "header" in
+ match elem with
+ | None -> 1
+ | Some _element -> 2
+```
+```reasonml
+type element;
+type document;
+[@mel.send] [@mel.return nullable]
+external get_by_id: (document, string) => option(element) = "getElementById";
+
+let test = document => {
+ let elem = get_by_id(document, "header");
+ switch (elem) {
+ | None => 1
+ | Some(_element) => 2
+ };
+};
+```
+
+Which generates:
+
+```js
+function test($$document) {
+ var elem = $$document.getElementById("header");
+ if (elem == null) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+```
+
+The `mel.return` attribute takes an attribute payload, as seen with `[@@mel.return nullable]``[@mel.return nullable]` above. Currently 4
+directives are supported: `null_to_opt`, `undefined_to_opt`, `nullable` and
+`identity`.
+
+`nullable` is encouraged, as it will convert from `null` and `undefined` to
+`option` type.
+
+
+
+`identity` will make sure that compiler will do nothing about the returned
+value. It is rarely used, but introduced here for debugging purposes.
diff --git a/scripts/advanced-js-interop.t b/scripts/advanced-js-interop.t
new file mode 100644
index 000000000..df829c562
--- /dev/null
+++ b/scripts/advanced-js-interop.t
@@ -0,0 +1,279 @@
+This test file is automatically generated from its corresponding markdown
+file. To update the tests, run `dune build @extract-code-blocks`.
+
+ $ cat > dune-project <