BuckleScript

BuckleScript

  • Docs
  • Try
  • API
  • Community
  • Blog
  • Languages iconEnglish
    • 日本語
    • Español
    • Français
    • 한국어
    • Português (Brasil)
    • Русский
    • 中文
    • 繁體中文
    • Help Translate
  • GitHub

›Interop

Intro

  • What & Why
  • Installation
  • New Project
  • Try
  • Concepts Overview
  • Upgrade Guide to v7

Interop

  • Overview
  • Cheatsheet
  • Embed Raw JavaScript
  • Common Data Types
  • Intro to External
  • Bind to Global Values
  • Null, Undefined & Option
  • Object
  • Object 2
  • Class
  • Function
  • Property access
  • Return value wrapping
  • Import & Export
  • Regular Expression
  • Exceptions
  • JSON
  • Pipe First
  • Generate Converters & Helpers
  • Better Data Structures Printing (Debug Mode)
  • NodeJS Special Variables
  • Miscellaneous
  • Browser Support & Polyfills

Build System

  • Overview
  • Configuration
  • Automatic Interface Generation
  • Interop with Other Build System
  • Performance
  • Advanced

Standard Library

  • Overview

Advanced

  • Conditional Compilation
  • Extended Compiler Options
  • Use Existing OCaml Libraries
  • Difference from Native OCaml
  • Compiler Architecture & Principles
  • Comparison to Js_of_ocaml
Edit

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 TypeJavaScript Type
intnumber
nativeintnumber
int32number
floatnumber
stringstring
arrayarray
tuplearray. (3, 4) -> [3, 4]
boolboolean
Js.Nullable.tnull/undefined
optionNone -> undefined
optionSome( Some .. Some (None)) -> internal representation
optionSome other -> other
recordobject. {x: 1; y: 2} -> {x: 1, y: 2}
special bs.deriving abstract recordobject

Non-shared

Again, the representations are subject to change.

OCaml/BS/Reason TypeJavaScript Value
int64array. [high, low]. high is signed, low unsigned
char'a' -> 97
bytesnumber 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.

Last updated on 4/13/2020
← Embed Raw JavaScriptIntro to External →
  • Shared Data Types
    • String
    • Float
    • Int
    • Array
    • Tuple
    • Bool
  • Records
  • Non-shared Data Types
    • Design Decisions
  • Cheat Sheet
    • Shared
    • Non-shared