Skip to content

Latest commit

 

History

History
192 lines (132 loc) · 6.73 KB

common-data-types.md

File metadata and controls

192 lines (132 loc) · 6.73 KB
title
Common Data Types

Shared Data Types

BuckleScript's primitives such as string, float, array and a few others have a rather interesting property: they compile to the exact same thing in JavaScript! Thanks to this, there's close to no API to learn for these data types.

This means that if you receive e.g. a string from the JS side, you can use it without conversion on the BS side, and vice-versa. In other words, string and others are "guaranteed public representations".

P.S. if you need an overview of the language primitives themselves, see the Reason docs.

BuckleScript uses the same standard library as OCaml; see the docs here (in Reason syntax). Additionally, we provide access to all the familiar JS primitives here. You can mix and match these two.

String

Immutable on both sides, as expected. BuckleScript String API. JS String API.

Unicode Support

OCaml string is an immutable byte sequence. If the user types some unicode:

Js.log "你好"
Js.log("你好")

It'll compile to the following JS:

console.log("\xe4\xbd\xa0\xe5\xa5\xbd");

Which gives you cryptic console output. To rectify this, BuckleScript exposes a special js annotation to the default quoted string syntax built into the language. Use it like this:

Js.log {js|你好,
世界|js}
Js.log({js|你好,
世界|js})

This'll correctly output:

console.log("你好,\n世界");

Interpolation

For convenience, we also expose another special tag quoted string annotation, j, which supports the equivalent of JS' string interpolation, but for variables only (not arbitrary expressions):

let world = {j|世界|j}
let helloWorld = {j|你好,$world|j}
let world = {j|世界|j};
let helloWorld = {j|你好,$world|j};

You can surround the variable in parentheses too: {j|你好,$(world)|j}.

Float

BuckleScript floats are JS numbers, vice-versa. The OCaml standard library doesn't come with a Float module. JS Float API is here.

Int

Ints are 32-bits! Be careful, you can potentially treat them as JS numbers and vice-versa, but if the number's large, then you better treat JS numbers as floats. For example, we bind to Js.Date using floats. Js Int API here.

Array

Idiomatic OCaml arrays are supposed to be fix-sized. This constraint is relaxed on the BuckleScript size. You can change its length using the usual JS Array API. BuckleScript's own Array API is here.

Tuple

OCaml tuples are compiled to JS arrays. Convenient when you're interop-ing with a JS array that contains heterogeneous values, but happens to have a fixed length. Model it as a tuple on the BS side!

Bool

Since BuckleScript 3, OCaml/Reason bool now compile to JS boolean.

Records

Since BuckleScript v7, OCaml/Reason records map directly to JS objects. If records contain any Non-Shared data types (like variants), then these values must be transformed separately and cannot be directly used in JS.

Non-shared Data Types

Variants (including option and list), BuckleScript objects and others can be exported as well, but you should not rely on their internal representation on the JS side. Aka, don't grab a BS list and start manipulating its structure on the JS side.

However, for BuckleScript related data types, we provide generation of converters and accessors. Once you convert e.g. variants to a string, you can naturally use them on the JS side.

For list, use Array.of_list and Array.to_list in the Array module. option will be highlighted shortly later on and also has its dedicated section as well.

For a seamless JS / TypeScript / Flow integration experience, you might want to use genType instead of doing convertion by hand.

Design Decisions

As to why we don't compile list to JS array or vice-versa, it's because OCaml array and JS array share similar characteristics: mutable, similar read/write performance, etc. List, on the other hand, is immutable and has different access perf characteristics.

The same justification applies for records. OCaml records are fixed, nominally typed, and in general doesn't work well with JS objects. We do provide excellent facilities to bind to JS objects in the object section.

Cheat Sheet

Shared

OCaml/BS/Reason Type JavaScript Type
int number
nativeint number
int32 number
float number
string string
array array
tuple array. (3, 4) -> [3, 4]
bool boolean
Js.Nullable.t null/undefined
option None -> undefined
option Some( Some .. Some (None)) -> internal representation
option Some other -> other
record object. {x: 1; y: 2} -> {x: 1, y: 2}
special bs.deriving abstract record object

Non-shared

Again, the representations are subject to change.

OCaml/BS/Reason Type JavaScript Value
int64 array. [high, low]. high is signed, low unsigned
char 'a' -> 97
bytes number array (we might encode it as buffer in NodeJS)
list [] -> 0, [x, y] -> [x, [y, 0]], [1, 2, 3] -> [ 1, [ 2, [ 3, 0 ] ] ]
Variant *
Polymorphic variant **
exception -
extension -
object -

* Variants with a single non-nullary constructor:

type tree = Leaf | Node of int * tree * tree
(* Leaf -> 0 *)
(* Node a b c -> [a, b, c] *)
type tree = Leaf | Node(int, tree, tree);
/* Leaf -> 0 */
/* Node a b c -> [a, b, c] */

Variants with more than one non-nullary constructor:

type u = A of string | B of int
(* A a -> [a].tag = 0 -- tag 0 assignment is optional *)
(* B b -> [b].tag = 1 *)
type u = A(string) | B(int);
/* A(a) -> [a].tag = 0 -- tag 0 assignment is optional */
/* B(b) -> [b].tag = 1 */

** Polymorphic Variant:

`a (* 97 *)
`a (1, 2) (* [ 97, [1, 2] ] *)
`a /* 97 */
`a(1, 2) /* [ 97, [1, 2] ] */

BuckleScript compiles unit to 0 in some cases but don't worry about it because it's considered internal and subject to change.