|
| 1 | + |
| 2 | + |
| 3 | +# BuckleScript |
| 4 | + |
| 5 | +Bringing OCaml to Javascript developers |
| 6 | + |
| 7 | +Hongbo Zhang, Bloomberg |
| 8 | + |
| 9 | +OCaml Meetup in Paris |
| 10 | + |
| 11 | +December 6, 2016 |
| 12 | + |
| 13 | +=== |
| 14 | + |
| 15 | + |
| 16 | +## Why BuckleScript? |
| 17 | + |
| 18 | +### Why OCaml |
| 19 | + |
| 20 | +- Save this slide for OCaml audience ^_^ |
| 21 | + |
| 22 | +--- |
| 23 | + |
| 24 | +### Why JS |
| 25 | + |
| 26 | +- Arguably the most used programming language |
| 27 | +- Everywhere and cross platform (Browser: the only language; Desktop |
| 28 | + App: Electron; Server: NodeJS, huge potential on [IoT](https://blogs.windows.com/buildingapps/2015/05/12/bringing-node-js-to-windows-10-iot-core/)) |
| 29 | +- NPM: largest package manager since 2014 |
| 30 | +- WebAssembly will make JS platform more attractive, for the first |
| 31 | + time cross platform FFI |
| 32 | +- Bloomberg is a big industry user of Javascript |
| 33 | + |
| 34 | +=== |
| 35 | + |
| 36 | +## Current status |
| 37 | + |
| 38 | +- BuckleScript is still a young project (started late 2015) |
| 39 | +- It covers the whole language, except libraries relying on c stubs |
| 40 | +- It grows fast (the most starred open source project by Bloomberg), |
| 41 | + already used by external users in production (Collaborations with |
| 42 | + Facebook Reason team) |
| 43 | + |
| 44 | +<blockquote style= 'background: #f9f9f9; ';> |
| 45 | + I'm on the Facebook Reason team, and we're using BuckleScript to compile OCaml into the best compiler output I've ever seen. People didn't recognize that my React components were generated, not hand-written. |
| 46 | +</blockquote> |
| 47 | + |
| 48 | + |
| 49 | +=== |
| 50 | + |
| 51 | +## Design goals (differences from Js_of_ocaml) |
| 52 | + |
| 53 | +Highlights of BuckleScript: |
| 54 | + |
| 55 | +- Easy FFI (OCaml -> JS, JS -> OCaml), incremental migration from JS |
| 56 | + to OCaml (One OCaml module <-> One JS module) |
| 57 | +- Human debuggable output (not relying on source-map) |
| 58 | +- Fast build (Save -> Compile -> Run: 100ms) |
| 59 | +- Native Windows support (same build experience) |
| 60 | + |
| 61 | +Advantages of Js_of_ocaml: |
| 62 | + |
| 63 | +- No changes to your existing OCaml build system |
| 64 | +- Less maintenance effort |
| 65 | + |
| 66 | + |
| 67 | +=== |
| 68 | + |
| 69 | +## A brief overview of compiler pipeline |
| 70 | + |
| 71 | + |
| 72 | +[Compiler pipelines](http://bloomberg.github.io/bucklescript/Manual.html#_high_level_compiler_workflow) |
| 73 | + |
| 74 | +=== |
| 75 | + |
| 76 | +## What does it look like? |
| 77 | + |
| 78 | +[Example: balanced tree](http://bloomberg.github.io/bucklescript/js-demo/#Balanced_tree) |
| 79 | + |
| 80 | + |
| 81 | +--- |
| 82 | + |
| 83 | +## Balanced tree with 2 million keys insertion and deletion |
| 84 | + |
| 85 | +Execution Time (node v7.2.0, BuckleScript + Google Closure, jsoo minify): |
| 86 | + |
| 87 | +- OCAMLOPT (-g): 0.837s |
| 88 | +- BuckleScript: 2.219s |
| 89 | +- JSOO: 3.035s |
| 90 | +- OCAMLC (-g): 6.545s |
| 91 | +- Using Facebook ImmutableJS lib: 13.520s |
| 92 | + |
| 93 | +JS output size: |
| 94 | +- BuckleScript: 542 bytes |
| 95 | +- JSOO: 3836 bytes |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | +=== |
| 100 | + |
| 101 | +## A brief look at FFI (Call OCaml from JS) |
| 102 | + |
| 103 | +- `.mli` is respected |
| 104 | +- Basic data types are closely matched (Array -> Array, Tuple -> Array, etc) |
| 105 | +- BuckleScript can also emit `.d.ts` files for TypeScript compiler (*experimental*) |
| 106 | +- [Publish and consume npm packages out of box](https://www.npmjs.com/package/bs-platform) |
| 107 | +- [Call OCaml library from JS side](http://bloomberg.github.io/bucklescript/js-demo/#Use_OCaml_Standard_Library) |
| 108 | + |
| 109 | +=== |
| 110 | + |
| 111 | +## FFI: (Calling Javascript from OCaml) |
| 112 | + |
| 113 | +Users must write *type declarations* for existing |
| 114 | +JavaScript libraries |
| 115 | + |
| 116 | +- Introducing built-in extension points and attributes |
| 117 | +- Structural typing (model JavaScript Objects) |
| 118 | +- Polymorphic variants (model Event handler) |
| 119 | +- Label and optional arguments (model JSON configuration) |
| 120 | + |
| 121 | + |
| 122 | +```ocaml |
| 123 | +external exp : float -> float = "Math.exp" [@@bs.val] |
| 124 | +let v = exp 3. |
| 125 | +``` |
| 126 | + |
| 127 | +```js |
| 128 | +var v = Math.exp(3.) |
| 129 | +``` |
| 130 | + |
| 131 | +--- |
| 132 | + |
| 133 | +## FFI examples |
| 134 | + |
| 135 | +Typescript binding: |
| 136 | + |
| 137 | +```ocaml |
| 138 | +type readline |
| 139 | +type line_callback = string -> unit [@bs] |
| 140 | +(* [bs] annotation to mark it as uncurried callback *) |
| 141 | +type close_callback = unit -> unit [@bs] |
| 142 | +external on : readline -> |
| 143 | + ([ `line of line_callback |
| 144 | + | `close of close_callback] |
| 145 | + [@bs.string]) -> unit = "" [@@bs.send] |
| 146 | +
|
| 147 | +let register readline = |
| 148 | + on readline (`line begin fun s -> prerr_endline s end); |
| 149 | + on readline (`close begin fun () -> prerr_endline "finished" end); |
| 150 | + print_endline "done" |
| 151 | +``` |
| 152 | + |
| 153 | +--- |
| 154 | + |
| 155 | +## FFI examples |
| 156 | + |
| 157 | +- In BuckleScript, `##` is used as method dispatch |
| 158 | + |
| 159 | +```ocaml |
| 160 | +let f obj = obj##height + obj##width |
| 161 | +val f : [%obj: < height : int ; width : int ; .. > ] -> int |
| 162 | +let a = f [%obj { height = 3; width = 32}] (* compiles *) |
| 163 | +let b = f [%obj {height = 3 ; width = 32; unused = 3 }] (* compiles *) |
| 164 | +``` |
| 165 | + |
| 166 | +--- |
| 167 | + |
| 168 | +## FFI examples (http server using Node.js) |
| 169 | + |
| 170 | +- `Test_http_server` |
| 171 | + |
| 172 | +```ocaml |
| 173 | +let port = 3000 |
| 174 | +let hostname = "127.0.0.1" |
| 175 | +let create_server http = |
| 176 | + let server = http##createServer begin fun [@bs] req resp -> |
| 177 | + resp##statusCode #= 200; |
| 178 | + resp##setHeader "Content-Type" "text/plain"; |
| 179 | + resp##end_ "Hello world\n" |
| 180 | + end |
| 181 | + in |
| 182 | + server##listen port hostname begin fun [@bs] () -> |
| 183 | + Js.log ("Server running at http://"^ hostname ^ ":" ^ Pervasives.string_of_int port ^ "/") |
| 184 | + end |
| 185 | +
|
| 186 | +let () = |
| 187 | + create_server Http_types.http |
| 188 | +``` |
| 189 | +--- |
| 190 | + |
| 191 | +## FFI examples (FFI bindings to NODEJS http module) |
| 192 | + |
| 193 | +- `http_types` |
| 194 | + |
| 195 | +```ocaml |
| 196 | +type req |
| 197 | +class type _resp = object |
| 198 | + method statusCode : int [@@bs.set] |
| 199 | + method setHeader : string -> string -> unit |
| 200 | + method end_ : string -> unit |
| 201 | +end [@bs] |
| 202 | +class type _server = object |
| 203 | + method listen : int -> string -> (unit -> unit [@bs]) -> unit |
| 204 | +end [@bs] |
| 205 | +type server = _server Js.t |
| 206 | +class type _http = object |
| 207 | + method createServer : (req -> resp -> unit [@bs] ) -> server |
| 208 | +end [@bs] |
| 209 | +type http = _http Js.t |
| 210 | +external http : http = "" [@@bs.module] |
| 211 | +``` |
| 212 | + |
| 213 | +--- |
| 214 | + |
| 215 | +### FFI (HTTP server) |
| 216 | + |
| 217 | +- Output for `http.ml` is empty (pure type declarations) |
| 218 | +- `Test_http_server` |
| 219 | + |
| 220 | +```js |
| 221 | +var Pervasives = require("bs-platform/lib/js/pervasives"); |
| 222 | +var Http = require("http"); |
| 223 | + |
| 224 | +var hostname = "127.0.0.1"; |
| 225 | + |
| 226 | +function create_server(http) { |
| 227 | + var server = http.createServer(function (_, resp) { |
| 228 | + resp.statusCode = 200; |
| 229 | + resp.setHeader("Content-Type", "text/plain"); |
| 230 | + return resp.end("Hello world\n"); |
| 231 | + }); |
| 232 | + return server.listen(3000, hostname, function () { |
| 233 | + console.log("Server running at http://" + (hostname + (":" + (Pervasives.string_of_int(3000) + "/")))); |
| 234 | + return /* () */0; |
| 235 | + }); |
| 236 | +} |
| 237 | + |
| 238 | +create_server(Http); |
| 239 | +``` |
| 240 | +=== |
| 241 | + |
| 242 | +## Easy to set up |
| 243 | + |
| 244 | +Installation |
| 245 | +``` |
| 246 | +npm install bs-platform |
| 247 | +``` |
| 248 | + |
| 249 | +Create a JSON file to describe the build spec: |
| 250 | + |
| 251 | +```js |
| 252 | +{ |
| 253 | + "name": "test", |
| 254 | + "sources": [ |
| 255 | + { |
| 256 | + "dir": "src" |
| 257 | + } |
| 258 | + ] |
| 259 | +} |
| 260 | + |
| 261 | +``` |
| 262 | + |
| 263 | +Build and run |
| 264 | + |
| 265 | +``` |
| 266 | +bsb -w |
| 267 | +``` |
| 268 | + |
| 269 | + |
| 270 | +=== |
| 271 | + |
| 272 | +## Different Semantics from other back-ends |
| 273 | + |
| 274 | + |
| 275 | + |
| 276 | +[Semantics diverge](http://bloomberg.github.io/bucklescript/Manual.html#_semantics_difference_from_other_backends) |
| 277 | + |
| 278 | + |
| 279 | +=== |
| 280 | + |
| 281 | +## Data representation |
| 282 | + |
| 283 | +[OCaml data representation in JS](http://bloomberg.github.io/bucklescript/Manual.html#_runtime_representation) |
| 284 | + |
| 285 | +=== |
| 286 | + |
| 287 | +## Wishes from OCaml compiler upstream |
| 288 | + |
| 289 | +- Tell the difference between block and array in lambda layer |
| 290 | + - OCaml Array has to be JS array for better FFI |
| 291 | + - More efficient data layout, useful for jsoo too |
| 292 | + |
| 293 | +--- |
| 294 | + |
| 295 | +## Wishes from OCaml compiler upstream |
| 296 | +- Native uncurried calling convention support |
| 297 | + - Essential for FFI in callbacks |
| 298 | + - BuckleScript attributes is leaky in error message, can not be Polymorpphic |
| 299 | + - Useful for jsoo too and external C bindings |
| 300 | + - Helps improve perf of native backend partial application |
| 301 | + - Built-in uncurying support can be smarter than ppx |
| 302 | + `'a -> int` === `'a -> int [@bs]` |
| 303 | + |
| 304 | +--- |
| 305 | + |
| 306 | +## Wishes from OCaml compiler upstream |
| 307 | + |
| 308 | +- More flexible lexical convention for method name, structual typing |
| 309 | + is useful but limited by syntax, name mangling is not cool |
| 310 | + |
| 311 | +```js |
| 312 | +{ "open" : true } |
| 313 | +{ "Content-Type" : "text"} |
| 314 | +``` |
| 315 | +--- |
| 316 | + |
| 317 | +## Wishes from OCaml compiler upstream |
| 318 | + |
| 319 | +- Polymorpphic variants as string (with immutable string optimization |
| 320 | + coming) |
| 321 | + - Useful in webprogramming, but generated unreadable code |
| 322 | +- Lift the char range limitation |
| 323 | + |
| 324 | +=== |
| 325 | + |
| 326 | +## Questions |
| 327 | + |
| 328 | +@bobzhang1988 |
0 commit comments