Skip to content

[doc] Move docs back to master branch #360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ boot
*.dump
coverage
*.g.js
dist/

man/
lib/ocaml
bin/*
Expand Down
2 changes: 2 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist/*.html
42 changes: 42 additions & 0 deletions docs/Compiler-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
BuckleScript inherits the command line arguments of the [OCaml compiler](http://caml.inria.fr/pub/docs/manual-ocaml/comp.html). It also adds several flags:

* -js-module

Specify the JavaScript module system to use in the generated JavaScript code. Supported values are `commonjs`, `amdjs` and `goog:<namespace>`

When you want to use `goog` module system, you can do things like this:

```bash
bsc -js-module goog xx.ml
# no namespace

bsc -js-module goog:bloomberg.buckle.test xx.ml
# namespace is bloomberg.buckle.test
```

You would then need a bundler for different the different module systems: `webpack` supports `commonjs` and `amdjs` while `google closure compiler` supports all.

* -js-gen-tds

Trigger the generation of TypeScript `.d.ts` files.

## Hello world

Currently, `BuckleScript` shares the same command line options as `ocamlc`
bytecode compiler.

Create a file called `hello.ml` as below

```sh
echo 'print_endline "hello world"' > hello.ml
```

```sh
bsc -c hello.ml
```

If everything goes well, you should have `hello.js`

```
console.log('hello world')
```
65 changes: 65 additions & 0 deletions docs/Compiler-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
## High Level compiler workflow

The high level architecture is illustrated as below:

```
Source Language
|
| (Reuse OCaml Parser)
v
Surface Syntax Tree
|
| (built in Syntax tree transformation)
v
Surface Syntax Tree
|
| (Reuse OCaml Type checker)
v
Typedtree
|
| (Reuse OCaml pattern match compiler and erase types)
v
Lambda IR (OCaml compiler libs) ---+
| ^ |
| | Lambda Passes (lam_* files)
| | Optimization/inlining/dead code elimination
| \ |
| \ --------------------------+
|
| Self tail call elimination
| Constant folding + propagation
V
JS IR (J.ml) ---------------------+
| ^ |
| | JS Passes (js_* files)
| | Optimization/inlining/dead code elimination
| \ |
| \ -------------------------+
|
| Smart printer includes scope analysis
|
V
Javascript Code
```

## Design Principles

The current design of BuckleScript follows several high level principles. While those principles might change in the future, there are enforced today and can explain certain technical limitations BuckleScript has.

**Lambda Representation**

As pictured in the diagram above BuckleScript is primarily based on the Lambda representation of the OCaml compiler. While this representation is quite rich, some information is lost from upstream representation. The patch to the OCaml compiler tries to enrich this representation in a non-intrusive way (see next section).

**Minimal Patch to the OCaml compiler**

BuckleScript requires patches to the OCaml compiler. One of the main reason is to enrich the Lambda representation so that the generated code is as nice as possible. A design goal is to keep those patches minimal and useful for the OCaml compiler in general so that they can later be integrated.

>A common question is to wonder why BuckleScript transpiles OCaml record value to JavaScript array while a more intuitive representation would be a JavaScript object. This technical decision is a direct consequence of the above 2 design principles: the Lambda layer assumes in a lot of places that a record value is an array and such modification would be too large of a change to OCaml compiler.

**Soundness**

BuckleScript preserves the soundness of the OCaml language. Assuming the FFI is correctly implemented, the type safety is preserved.

**Minimal new symbol creation**

In order to make the JavaScript generated code as close as possible to the original OCaml core we thrive to introduce as little new symbols as possible.
13 changes: 13 additions & 0 deletions docs/Create-a-simple-example-with-NPM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

Assume you have the required OCaml compiler installed, see [Installation](./Installation).

Create a temporary directory called `npm_test`

```sh
cd npm_test
echo "{}" > package.json
npm install --save bs-platform
echo 'let _ = Js.log "hello bucklescript!"' > hello.ml
./node_modules/.bin/bsc -I ./node_modules/bs-platform/ -c hello.ml
node hello.js
```
12 changes: 12 additions & 0 deletions docs/Curry-and-Uncurry-functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
JavaScript functions are all [uncurried](https://en.wikipedia.org/wiki/Currying), while OCaml functions are curried by default.

A naive approach would be to Curry every OCaml functions but this has a non trivial cost and makes the generated code harder to read. Therefore BuckleScript will try its best to uncurry its function in both definition and call site.

It is also possible to help BuckleScript perform the uncurrying by using using the runtime provided `Fn` module:

```OCaml
let iter_f = Fn.mk1 my_function in
List.iter iter_f l
```


23 changes: 23 additions & 0 deletions docs/Dev-mode-How-to.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
### Build the compiler

The development of BuckleScript compiler relies on 2 tools which are readily available in `opam` and work with our patch OCaml compiler:
- [ocamlbuild](http://caml.inria.fr/pub/docs/manual-ocaml-400/manual032.html): Default build tool for OCaml project
- [camlp4](https://github.com/ocaml/camlp4): Tool used to generate OCaml code for processing large AST. (j.ml file).

After having installed the above dependencies from opam you can run the following:

```sh
cd jscomp/
./build.sh
```

### Build the runtime

```sh
cd ./runtime; make all
```
### Build the stdlib

```sh
cd ./stdlib; make all
```
59 changes: 59 additions & 0 deletions docs/Differences-from-js_of_ocaml.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[js_of_ocaml](https://github.com/ocsigen/js_of_ocaml) is a popular compiler which compiles OCaml's bytecode into JavaScript. It is the inspiration for this project, and has already been under development for several years and is ready for production. In comparison, BuckleScript, while moving fast, is still a very young project. BuckleScript's motivation, like `js_of_ocaml`, is to unify the ubiquity of the JavaScript platform and the truly sophisticated type system of OCaml, however, there are some areas where we view things differently from `js_of_ocaml`. We describe below, some of these differences, and also refer readers to some of the original informal [discussions](https://github.com/ocsigen/js_of_ocaml/issues/338).

## Debuggable Output

One of the main BuckleScript goal is to generate debuggable JavaScript code. From the start we believed developers using BuckleScript for JavaScript development will look at the generated code an order of magnitude more than an OCaml developer will look at the assembly one. Part of our intent is to make it easier for a JavaScript developer to transition to the OCaml language. Furthermore looking at existing transpilers for JavaScript: [coffescript](http://coffeescript.org/), [babel](https://babeljs.io/) and [typescript](https://github.com/Microsoft/TypeScript), the most widely adopted ones are the one that favors code readability.
The generated code by BuckleScript is pretty close to the JavaScript code one might write by hand, especially if you use mostly the language features which are shared between JavaScript and OCaml.

`js_of_ocaml` produces code with mangled names, which is typically not a concern, except when the code is used as a primary backend and must be extensively debugged and maintained. In these situations, `js_of_ocaml` proves harder to debug, because the original names in the code are lost.

## Runtime Representations && FFI

**Runtime Representation**

`js_of_ocaml` runtime representation is as close as possible to the runtime representation of native/byte compiler. This makes it particularly easy for an OCaml developer already familiar with that representation.

BuckleScript runtime representation is closer to JavaScript. This is particularly helpful for both the ability to debug OCaml values in JavaScript but also when writing the FFI.

Some example of differences are:
- `string` : In BuckleScript the string is represented as a JavaScript string while in `js_of_ocaml` it's a much more involved data structure.
- `array`: In BuckleScript the array has the same indexing as a JavaScript array while for `js_of_ocaml` the index starts at 1.
- `tuple`: In BuckleScript the array has the same indexing as a JavaScript array while for `js_of_ocaml` the index starts at 1.

**FFI**

Regarding the FFI, BuckleScript favors using only [attributes](http://caml.inria.fr/pub/docs/manual-ocaml/extn.html#sec245); one drawback that we plan to address on the future is that it lacks some expressiveness.

`js_of_ocaml` introduces a very sophisticated and expressive syntax extension, which works great most of the time, but can sometimes generate confusing compiler errors, and be difficult to integrate with existing IDEs and build systems.

**Runtime library**

BuckleScript runtime library is mostly implemented in OCaml while `js_of_ocaml` runtime is purely in JavaScript. The difference is actually significant since we believe it reflects how easy it is to write JavaScript code in OCaml using the BuckleScript model (FFI and Runtime representation).

## Separate Compilation

BuckleScript compiles one OCaml module into one Javascript module. This follows the modern JavaScript development and makes it easier to gradually use OCaml in an existing code base. This is particularly relevant for our use cases for which we plan to implement components in OCaml while the larger application is written in JavaScript. The JavaScript echo system also has plenty of tools and support for modules, making it easy and natural to integrate modules generated by BuckleScript. It also opens the door for supporting [hot module replacement](http://webpack.github.io/docs/hot-module-replacement.html) in future.
The module compilation strategy allows more granular compilation and as consequence faster feedback loop during development.

`js_of_ocaml` compiles a whole OCaml program in JavaScript. This makes it easier when the main application is written in OCaml but less natural when integrating OCaml as a component in a larger JavaScript application.

## Integration with existing ecosystems

BuckleScript is aware of `npm` module paths and favor libraries development. In contrast with `js_of_ocaml`' focus on developing executable.
BuckleScript does not provide a whole program compilation mode out of the box; it rather delegates this to existing linker/bundler (such as webpack or Google Closure compiler).

When integrating code generated by BuckleScript developers only need to look at the `.mli` files. BuckleScript respect those interfaces and will only export the functions from the `.mli file. No special code needs to be added to expose OCaml code to the JavaScript.

`js_of_ocaml` requires specific code to export functions OCaml code to the larger JavaScript application.

## Intrusiveness

The major advantage of `js_of_ocaml` is that it is non intrusive; you can reuse your existing project setup (build system) and simply convert the bytecode output to JavaScript output as a post-processing step.

With BuckleScript, however, users have to adapt their build system for existing OCaml code base. In BuckleScript, we traded intrusiveness for being able to extract more information from the code.

We think there's a need for both tools to exist simultaneously. If you have some OCaml code, and your primary motivation is that it should run inside a browser, then `js_of_ocaml` is a very good choice. However, if you target JavaScript (browser/Node.Js) as the primary backend for your application and care about the integration with the JavaScript environment then we believe BuckleScript can offer a better experience. We also think both projects can help and learn from each other.

## Performance

We have not conducted exhaustive benchmarks yet, however, our initial results show similar performance between `js_of_ocaml` and BuckleScript (using Google Closure simple optimization level).
1 change: 1 addition & 0 deletions docs/Experimental-support-of-typescript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`bsc` has the ability to also emits `.d.ts` for better interaction with typescript. This is still experimental.
68 changes: 68 additions & 0 deletions docs/Extensions-to-OCaml-Language.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
BuckleScript leverages the OCaml support for extension with dedicated builtin extensions. Those BuckleScript extensions facilitates the integration of native JavaScript code as well as improve the generated code.

> Note that all those extension will be correctly ignored by the native OCaml compiler.

1. extension `bs.raw`

It can be either `[%bs.raw{| this_is_arbitrary_js_expression |}]` or `[%%bs.raw{| this is arbitrary_js_statement |}`

Use cases:
for example if you want to use a JavaScript string, you can write code like this

```OCaml
let x : string = [%bs.raw{|"\x01\x02"|}]
```

which will be compiled into

```js
var x = "\x01\x02"
```

```OCaml
[%%bs.raw{|
// Math.imul polyfill
if (!Math.imul){
Math.imul = function (..) {..}
}
|}]
```
In the expression level, i.e, `[%js.raw ...]` user can add a type annotation, the compiler would use such type annotation to deduce its arities. for example, the next three versions:

```ocaml
let f = [%bs.raw ("Math.max" : float -> float -> float) ] 3.0
let f : float -> float -> float = [%bs.raw "Math.max" ] 3.0
let f = ([%bs.raw "Math.max"] : float -> float -> float ) 3.0
```
will be translated into

```js
function f(prim){
return Math.max(3.0,prim);
}
```
Caveat:
1. So far we don't do any sanity check in the quoted text (syntax check is a long-term goal)
2. You should not refer symbols in OCaml code, it is not guaranteed that the order is correct.
You should avoid introducing new symbols in the raw code, if needed, use the `$$` prefix (ie `$$your_func_name`)

2. extension `bs.debugger`

It can be `[%bs.debugger]`

use case

```ocaml
let f x y =
[%bs.debugger];
x + y
```

which will be compiled into

```js
function f (x,y) {
debugger; // JavaScript developer tools will set an breakpoint and stop here
x + y;
}
```
4 changes: 4 additions & 0 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

* The compiler does not build

In production mode, the compiler is a single file in `jscomp/bin/compiler.ml`, if it is not compiling, make sure you have the right OCaml compiler version. Currently OCaml Compiler is a submodule of bucklescript. Make sure the exact commit hash matches (we only update the compiler occasionally).
1 change: 1 addition & 0 deletions docs/Help-continuous-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Help is appreciated!
39 changes: 39 additions & 0 deletions docs/Help-move-runtime-functions-from-OCaml-to-Javascript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
BuckleScript runtime implementation is currently a mix of OCaml and JavaScript. (jscomp/runtime directory). The JavaScript code is defined in the `.ml` file using the `bs.raw` syntax extension.

The goal is to implement the runtime **purely in OCaml** and you can help contribute.

Each new PR should include appropriate testing.

Currently all tests are in `jscomp/test` directory and you should either add a new test file or modify an existing test which covers the part of the compiler you modified.

* Add the filename in `jscomp/test/test.mllib`

* Add a suite test

The specification is in `jscomp/test/mt.ml`

For example a simple tests would be like

```ocaml
let suites : _ Mt.pair_suites = Mt.[
"hey", (fun _ -> Eq(true, 3 > 2));
"hi", (fun _ -> Neq(2,3);
"hello", (fun _ -> Approx(3.0, 3.0));
"throw", (fun _ -> ThrowAny(fun _ -> raise 3))
]
let () = Mt.from_pair_suites __FILE__ suites
```

* Run the test

Suppose you have mocha installed, if not, try `npm install mocha`

```
mocha -R list jscomp/test/your_test_file.js
```

* See the coverage

```
npm run cover
```
34 changes: 34 additions & 0 deletions docs/How-to-adapt-your-build-system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
BuckleScript compilation model is the same as the OCaml compiler.

If `b.ml` depends on `a.ml`, you have to compile `a.ml` **and** `a.mli` first.

> The technical reason is that BuckleScript will generate intermediate file with extension `.cmj` which are later used for cross module inlining and other information.

Find below a simple Makefile to get started:

```make
OCAMLC=bsc
# bsc is BuckleScript compiler
OCAMLDEP=ocamldep
# ocamldep executable is part of the OCaml compiler installation

SOURCE_LIST := src_a src_b
SOURCE_MLI = $(addsuffic .mli, $(SOURCE_LIST))
SOURCE_ML = $(addsuffic .ml, $(SOURCE_LIST))

TARGETS := $(addsuffix .cmj, $(SOURCE_LIST))

INCLUDES=

all: $(TARGETS)

.mli.cmi:
$(OCAMLC) $(INCLUDES) $(COMPFLAGS) -c $<
.ml.cmj:
$(OCAMLC) $(INCLUDES) $(COMPFLAGS) -c $<

-include .depend

depend:
$(OCAMLDEP) $(INCLUDES) $(SOURCE_ML) $(SOURCE_MLI) | sed -e 's/\.cmx/.cmj/g' > .depend
```
Loading