What is Bucklescript?
Bucklescript is an OCaml to Javascript transpilier - it allows you to “write JS faster, safer and smaller”.
Why use Bucklescript, instead of other options like say, Elm?
Due to the huge ecosystem behind it, OCaml, opam, and a few large companies (Bloomberg, Facebook), it will have staying power the likes of which those newer languages have trouble with. It has the full power of the OCaml language, of which can be used with a simplified syntax almost identical to that of Elm to far more that will not restrict you in getting actual work done. Combined with its ability to call Javascript and vice-versa in clean ways while generating clean and readable and efficient code that is fully typed will make working on the front-end significantly better, easier, and more maintainable.
Have a look at this thread.
Where can I see some examples of it?
Here: Testing
Here is the original post of this thread before it was made into a wiki - it goes into more detail:
So I’d mentioned before that I’ve wanted a fully typed language to use instead of javascript, and I picked Elm because it was the most ‘complete’, although Purescript looks better from an implementation perspective, well as of a few days ago another language appeared as version 1.0 (1.0.1 at the time of this post) that compiles the entirety of OCaml in such a way that makes it interoperable with Javascript, both ways by using an OCamls FFI syntax, it is Bucklescript.
OCaml is an ancient ML derivative that is ‘object’ based, so its fits in well with Javascript.
A couple of code examples (mostly from their docs) for those of you, who like me, like seeing how a language is by its code:
##Hello World
OCaml source:
print_endline "Hello BuckleScript!"
Output javascript:
// Generated by BUCKLESCRIPT VERSION 1.0.1 , PLEASE EDIT WITH CARE
'use strict';
console.log("Hello BuckleScript!");
/* Not a pure module */
First thing to notice is that it really does output pure javascript, no including of 600kb+ of ‘standard unused library’ like with Elm, and there are sensible conversions from standard OCaml calls to javascript. Also note a comment at the end saying whether it is or is not a pure module, good documentation.
Function definitions
OCaml source:
let test0 =
42
let test1 i =
i + test0
let test2 =
test1 18
let test3 f i =
f i
let test4inc i =
test3 test1
let test4 i =
test3 test1 i
let test4explicitlytyped : int -> int =
fun i -> test3 test1 i
let test4inctyped : ( int -> int ) -> ( int -> int ) =
fun i -> test3 test1
let test5 f =
f##hi 1
let test6 f =
let u = f##hi in
u 1
let test7 f =
let u = f##hi in
u 1 [@bs]
Output javascript:
// Generated by BUCKLESCRIPT VERSION 1.0.1 , PLEASE EDIT WITH CARE
'use strict';
var Curry = require("stdlib/curry");
function test1(i) {
return i + 42 | 0;
}
var test2 = 60;
function test3(f, i) {
return Curry._1(f, i);
}
function test4inc() {
return function (param) {
return param + 42 | 0;
};
}
function test4(i) {
return i + 42 | 0;
}
function test4explicitlytyped(i) {
return i + 42 | 0;
}
function test4inctyped() {
return function (param) {
return param + 42 | 0;
};
}
function test5(f) {
return f.hi(1);
}
function test6(f) {
var u = f.hi;
return Curry._1(u, 1);
}
function test7(f) {
var u = f.hi;
return u(1);
}
var test0 = 42;
exports.test0 = test0;
exports.test1 = test1;
exports.test2 = test2;
exports.test3 = test3;
exports.test4inc = test4inc;
exports.test4 = test4;
exports.test4explicitlytyped = test4explicitlytyped;
exports.test4inctyped = test4inctyped;
exports.test5 = test5;
exports.test6 = test6;
exports.test7 = test7;
/* No side effect */
Quite a lot of things going on here. First I notice that it states the module is side effect free, which is great. Second I notice the exports, meaning this can be put in as an ES6 module (and indeed the compiler does output to the module format, a module per ocaml module, 1-to-1 mapping), and it exports all the functions that I left externally visible, with the same names.
I tested a lot of different simple styles of functions, ones that just return a value are not functions but rather constants, and that is indeed reflected in the output, as can be seen in test0
, it also optimizes down a path so test2
becomes constant too. Interestingly test1
takes an integer and returns an integer, but since it is a publicly exported function (I tested private functions and it did not do this) it makes certain it always returns an integer, always, by returning 0 if the i + 42
happened to fail for some reason (like i
not being a number in javascript), I like this design decision (I’m curious if it is possible to change the default, a wrapper function could for sure at least).
I then tested some currying, a plain curry in test3
involves a library call (an stdlib/curry
javascript module is output), the reason for this is in case the passed in function took more than one argument, so it does not know if it is returning some value or returning another function to be curried further. If the code was pure ocaml calls then it can optimize that out, but for javascript integration it leaves this little function call here for them. In test4
I test calling test3 with a function and do indeed see the Curry._1
call gets optimized out. I was curious though and made a test4inc
that only partially applied the function, and fascinatingly I find it also optimized it out. I also made a test4explicitlytyped
function that was completely typed so I could see how that changed, no changes from test4
so the default value was still left there for javascript purposes. Typing the curried version had no change either.
I then tried out some of the new special constructs that Bucklescript adds to OCaml, starting with ##
, which becomes the field get operator in javascript (I.E. .
). test5
was the most simple test of that, easily converting f##hi 1
into f.hi(1)
. I then curry that to see how it acts, and it indeed puts in the Curry._1
call again, however it has some attributes that you can add to expressions to do various things, one of those is adding [@bs] to the call to tell it to fully ‘resolve’ the function call, in other words no currying, it will fully call it. This attribute is mostly for javascript compat as normal ocaml code will resolve it out properly in most cases.
Matching
OCaml source:
let matching v = match v with
(true,true) -> true
| (true,false) -> false
| (false,true) -> true
| (false,false) -> true;;
Output javascript:
// Generated by BUCKLESCRIPT VERSION 1.0.1 , PLEASE EDIT WITH CARE
'use strict';
function matching(v) {
if (v[0] !== 0 && v[1] === 0) {
return /* false */0;
}
else {
return /* true */1;
}
}
exports.matching = matching;
/* No side effect */
Seems quite correct.
OCaml class
OCaml source:
class ['a] stack =
object (self)
val mutable list = ( [] : 'a list ) (* instance variable *)
method push x = (* push method *)
list <- x :: list
method pop = (* pop method *)
let result = List.hd list in
list <- List.tl list;
result
method peek = (* peek method *)
List.hd list
method size = (* size method *)
List.length list
end
Output javascript:
// Generated by BUCKLESCRIPT VERSION 1.0.1 , PLEASE EDIT WITH CARE
'use strict';
var CamlinternalOO = require("stdlib/camlinternalOO");
var List = require("stdlib/list");
function stack_init($$class) {
var ids = CamlinternalOO.new_methods_variables($$class, [
"size",
"push",
"pop",
"peek"
], ["list"]);
var size = ids[0];
var push = ids[1];
var pop = ids[2];
var peek = ids[3];
var list = ids[4];
CamlinternalOO.set_methods($$class, /* array */[
push,
function (self$neg9, x) {
self$neg9[list] = /* :: */[
x,
self$neg9[list]
];
return /* () */0;
},
pop,
function (self$neg9) {
var result = List.hd(self$neg9[list]);
self$neg9[list] = List.tl(self$neg9[list]);
return result;
},
peek,
6,
List.hd,
list,
size,
6,
List.length,
list
]);
return function (_, self) {
var self$1 = CamlinternalOO.create_object_opt(self, $$class);
self$1[list] = /* [] */0;
return self$1;
};
}
var stack = CamlinternalOO.make_class([
"peek",
"push",
"size",
"pop"
], stack_init);
exports.stack = stack;
/* stack Not a pure module */
It seems to make a proper javascript class, returning javascript object constructor that makes what you would expect
A Map
OCaml source:
module StringMap = Map.Make(String)
let stringMap =
let map = StringMap.empty
in StringMap.add "aKey" "someValue" map
Output javascript:
// Generated by BUCKLESCRIPT VERSION 1.0.1 , PLEASE EDIT WITH CARE
'use strict';
var $$Map = require("stdlib/map");
var Curry = require("stdlib/curry");
var $$String = require("stdlib/string");
var StringMap = $$Map.Make([$$String.compare]);
var stringMap = Curry._3(StringMap[/* add */3], "aKey", "someValue", StringMap[/* empty */0]);
exports.StringMap = StringMap;
exports.stringMap = stringMap;
/* StringMap Not a pure module */
So it has its own map module with its interface, and it does appear to be immutable as it should be, nice.
List and list operation
OCaml source:
let multiply n list =
let f x = n * x
in List.map f list
let testing0 l =
multiply 42 l
let testing1 =
testing0 [ 1; 2; 3 ]
Output javascript:
// Generated by BUCKLESCRIPT VERSION 1.0.1 , PLEASE EDIT WITH CARE
'use strict';
var Caml_int32 = require("stdlib/caml_int32");
var List = require("stdlib/list");
function multiply(n, list) {
var f = function (x) {
return Caml_int32.imul(n, x);
};
return List.map(f, list);
}
function testing0(l) {
return multiply(42, l);
}
var testing1 = multiply(42, /* :: */[
1,
/* :: */[
2,
/* :: */[
3,
/* [] */0
]
]
]);
exports.multiply = multiply;
exports.testing0 = testing0;
exports.testing1 = testing1;
/* testing1 Not a pure module */
Got more ocaml modules here, everything is expected and it is interesting to see that it uses Cons cells for lists in javascript too.
Calling a javascript thing
OCaml source:
external create_date : unit -> 't = "Date" [@@bs.new]
let date = create_date ()
Output javascript:
// Generated by BUCKLESCRIPT VERSION 1.0.1 , PLEASE EDIT WITH CARE
'use strict';
var date = new Date();
exports.date = date;
/* date Not a pure module */
Other than the horrible syntax coloring here it all looks perfect.
I’m still unsure which has the better syntax as of yet, Bucklescript or Purescript, but Bucklescript has the entire power of ocaml and its libraries (that do not include native calls) at its disposal while being able to compile down to javascript or native (via llvm) code, and it has an in-browser compiler that you can include if you want anything dynamic. (See it in use here).
Purescript, however, looks to have a better syntax, removing old warts and such, however it has no build system that I was able to find for brunch and it has a large lack of libraries still.
At this point I am leaning toward Bucklescript. It would actually be pretty easy to take a virtualdom and let Bucklescript use it while also optimizing the virtualdom to be able to make a lot of expectations of immutability in its structure to gain the same speed that Elm has. It is very new too still, thus lacking a lot of ‘specific’ libraries that will be useful later, as well as missing some use-case specific tooling, although it already has all of ocaml’s extensive tooling.
This looks like it will be quite interesting though and I will be watching it.