Calling JavaScript
When running on a JS host (the browser, Node, Bun), Raven can call JavaScript directly – no bindings or glue code. This is the main way Raven programs reach the outside world today: the DOM, npm packages, files, timers.
The js function
js converts Raven objects to JavaScript ones. Use the results largely as you would in JS proper – property access, method calls, new:
> js("hello")
js("hello")
> js("hello").toUpperCase()
js("HELLO")
You can also use js like a namespace; it represents globalThis:
> js.Math.sqrt(5)
js(2.23606797749979)
Calling JS results in (boxed) JS objects, not Raven ones, so you'll usually want to convert back:
> String(js("hello").toUpperCase())
"HELLO"
> Float64(js.Math.sqrt(5))
2.23606797749979
Construct JS objects with new, and load modules with import:
encoder = new(js.TextEncoder)
fs = import("fs/promises")
Inline JavaScript
You can write JavaScript inline with js template strings, interpolating Raven values with \:
fn mysqrt(x) {
result = js`return Math.sqrt(\x)`
return Float64(result)
}
The code is evaluated in a function context, so you need to return to get a value back. Triple-quote templates work for longer snippets:
fn sleep(n) {
p = ```js
return new Promise(resolve => {
setTimeout(() => { resolve() }, \n * 1000)
})
```
await(p)
return
}
Promises, without function colouring
Unlike JS, Raven has no async/await keywords, and no distinction between async and sync functions. We still unwrap JS promises with await, but it's a normal function call – any function can use it, and callers don't know or care:
fn readFile(f: String) {
fs = import("fs/promises")
return String(await(fs.readFile(f, "utf8")))
}
That sleep above is the same story: it blocks its caller for n seconds, whoever that caller is, with no viral async annotations up the call chain. To run something concurrently rather than waiting, wrap it in the async macro, which returns a task:
async {
slowThing()
}
(Concurrency is young – tasks and channels exist but are still settling. See Status.)
Converting data
Conversions are explicit in both directions. js(x) goes out; type constructors (String, Float64, Int32, map(..., UInt8) and so on) come back:
> s = "foo"
> map(new(js.TextEncoder).encode(s), UInt8)
[0x66, 0x6f, 0x6f]
> map(js`return new TextEncoder().encode(\s)`, UInt8)
[0x66, 0x6f, 0x6f]
JS arrays are iterable from Raven, which is why map works on them directly.