JavaScript

Building JavaScript

To build to JavaScript, run a command like keen build foo.keen --out foo.js.
Then include it in a web page using <script type="module" src="/foo.js"></script> .

You can also run node foo.js . Node.js support is mostly intended for quick testing and not as the main build target.

Calling JavaScript functions

Now we'll write "Hello, world" directly using JavaScript APIs.

import keen/js main void() js, global, unsafe alert js-any = js-global["alert"] _ = call alert, "Hello, world!"

The js-any type can hold any JavaScript value. Basically any function using this type is unsafe.

js-global is equivalent to window (or self) in the browser or global in Node.js .

subscript reads a property from a JS object. So js-global["alert"] is equivalent to window.alert in JS.

call is a JS function call. So call alert, "Hello, world!" is equivalent to alert("Hello, world!") in JS.

call returns js-any. _ = drops the js-any value to get void.

Use JavaScript objects

To call a method on a JavaScript object, use call-property.

import keen/js keen/js/dom main void() js, global, unsafe home = call-property document, "querySelector", "#test-p" home["style"]["border"] := "1px solid pink"

This paragraph has ID test-p. After running the above script it will have a border.

The first line above is equivalent to document.querySelector("#test-p") in JS.
The second line is equivalent to home.style.border = "1px solid pink"; in JS.

Conversion

There is no guarantee about how Keen values are represented in JS.
For example, a Keen string is not a JS string.

keen/js defines many to functions for converting between Keen and JS values.

import keen/js main void() js n nat = 1 j js-any = n.to log j.to::nat

Make JavaScript objects

named-new from keen/js/util is useful for creating JS objects.

import keen/js keen/js/util main void() js, unsafe o js-any = x: 1, y: 2 log o["x"].to::nat

Async

To call a JavaScript async function, simply call and await it.

import keen/js main void() js, global, unsafe log fetch-text "/hello.txt" fetch-text string(url string) js, global, unsafe fetch = js-global["fetch"] response = await call fetch, url.to to::string await call-property response, "text"

For best performance, try to avoid doing async things in a deeply nested functions, since this makes their callers (and callers' callers, etc.) async functions too.

Callbacks

A Keen lambda can be converted to a JS function using to.

These functions support lambdas taking void or js-any and returning void or js-any.

import keen/js main void() js, global sleep 5000 log "done" sleep void(msec float) global, js do trusted resolver = of resolve js-any :: void _ = call js-global["setTimeout"], resolve, msec.to _ = await call-new js-global["Promise"], resolver.to

Imports

Keen does not yet have the ability to import JavaScript modules directly.
There is a js-import function though.

import keen/js keen/js/util main void() js, global, unsafe module = js-import "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.3/+esm" # This calls the default export _ = call-property module, "default", particleCount: 100 spread: 100

Other JavaScript operations

If Keen doesn't have a function for doing what you want with JS, you can write a JS snippet using eval.

import keen/js main void() js, unsafe log loose-equal 1, "1" loose-equal bool(a js-any, b js-any) js, unsafe f = eval quote (x, y) => x == y to::bool call f, a, b