Skip to content

A BuckleScript syntax extension to resemble Javascript's async/await

License

Notifications You must be signed in to change notification settings

etanol/bs-async

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bs-async

A BuckleScript syntax extension to resemble Javascript async/await.

IMPORTANT: This is an experimental package. The semantics of code transformations are not stabilized yet. The package is public in order to gather some feedback.

Overview

This package implements a PPX to transform OCaml/Reason code that almost looks like Javascript's async/await into code that chains promises.

The available extensions are %async and %async'. The difference between the two is that the former automatically wraps some parts of the expression into a Js.Promise.t and the latter expects all parts of the expression to be of Js.Promise.t.

let expressions

Original:

let%async name = promise in expression

Result (approximate):

Js.Promise.then_ (fun name -> expression) promise

Using %async forbids expression from being of Js.Promise.t type, whereas %async' requires it.

It also works with multiple bindings. Original:

let%async first = promise1
and second = promise2
in
expression

Result (approximate):

Js.Promise.then_
  (fun [| first ; second |] -> expression)
  (Js.Promise.all [| promise1 ; promise2 |])

try expressions

Original:

try%async
  promise
with
| pattern1 -> expression1
| pattern2 -> expression2

Result (approximate):

Js.Promise.catch
  (function pattern1 -> expression1 | pattern2 -> expression2 | e -> propagate e)
  (Js.Promise.then_
    (fun () -> promise)
    (Js.Promise.resolve ()))

Again, %async would forbid expression1 and expression2 from having a Js.Promise.t type, whereas %async' would require it. Also, in %async the propagate e expression becomes raise e while in %async'is just Js.Promise.reject e. This catch all case is automatically appended with a warning silencer if the existing cases in the code already cover all possibilities.

The extra convolution of the generated code attempts to catch any exception thrown within the try body asynchronously. If, instead, this PPX generated code like the following:

(* Bogus hipothetically generated code *)
Js.Promise.catch
  (function pattern1 -> expression1 | pattern2 -> expression2)
  promise

What would happen is that it would break expectations in some cases (both kind of expectations, JS and OCaml). Like, for example:

exception My_salsa of string

let fail x : string -> string=
  try%async
    raise (My_salsa x) |> Js.Promise.resolve
  with
  | My_salsa e -> Js.log e

The fail function should return a rejected promise. However, depending on how it's used, it could raise an OCaml exception immediately.

match expressions

Original:

match%async promise with
| pattern -> expression

Note that this, in practice, could simply desugar to:

let%async name = promise in
match name with
| pattern -> expression

However, it directly generates something like the following:

Js.Promise.then_
  (function pattern -> expression)
  promise

More interesting, though, is when exception cases are used:

match%async promise with
| pattern -> expression
| exception ex -> handler

Which translates (approximately) to:

Js.Promise.then_catch  (* Mapping for two arguments Promise.then() *)
  (function pattern -> expression)
  (function ex -> handler | e -> propagate e)
  promise

In depth discussion

For now, see the design notes separately available.

About

A BuckleScript syntax extension to resemble Javascript's async/await

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published