For the complete documentation index, see llms.txt. This page is also available as Markdown.

Example Generator

Let's use UrWasm to write a generator that can quickly sort a large list of 64-bit integers in ascending order.

Benchmark without UrWasm

In pure Hoon, we would write something like this:

|=  lit=(list @G)
^-  (list @G)
~>  %bout
(sort lit lth)

Let's run this and see how long it takes. (__~ in the Dojo discards the product of the given expression and returns ~).

> =l (flop (gulf 0 1.000))
>
took ms/63.434
> __~ +run l
~

Building the Wasm module

Now let's sort the list using UrWasm, with the source code written in Rust. Initialize a new library cargo with cargo new wasm_sort --lib and edit Cargo.toml:

[package]
name = "wasm_sort"
version = "0.1.0"
edition = "2024"

[dependencies]
wasm-bindgen = "0.2"

[lib]
crate-type = ["cdylib"]

Paste this source code into sort.rs:

Run wasm-pack build and wasm-pack will compile the wasm_sort.wasm module from this file. The wasm_bindgen Rust library will be used to create a corresponding JS bindings file named something like wasm_sort_bg.js.

Writing Hoon bindings

Let's see how the JS bindings file calls sort_u64(). Remember, this is a generated wrapper function that would be called from the web app. This wrapper function is what we'll have to reimplement in our new Hoon generator to call out to the compiled wasm_sort.wasm module.

What's going on in this sortu64() wrapper function? We see that it does the following:

  1. Allocates memory for the input vector by calling __wbindgen_malloc.

  2. Writes the contents of the array to Wasm memory.

  3. Calls sort_u64() with the array pointer and length as parameters, which returns two values.

  4. Uses those two values as the pointer and length of the resulting array, reads that from memory.

  5. Frees the returned array from Wasm memory with __wbindgen_free.

  6. Returns the sorted array.

We don't need to reimplement step 5, since the whole Wasm VM will be freed when we're done.

Our generator with Hoon "bindings" will look like this in full. We'll examine each part in detail below.

What's going on here?

First, we import the Lia interpreter and the .wasm module, which we've copied in to the root of our desk. (If you're working through this example, the %base desk on a fakeship would be fine.)

Mostly generator boilerplate, but note the .lit parameter and the output (list @G). (That is, a +list of @s where G indicates a bitwidth of 64.)

We use the %bout runtime hint to time the computation that follows.

Now we'll define the types for our yield of the main script, and the accumulator noun:

  • The $yil-mold is the type of our yield, the result of the script.

  • The $acc-mold is the type of the accumulator, which holds some arbitrary state we can read and write to during script execution.

We don't need the accumulator for this example but it's required for +run-once, so we'll just call it a noun *.

Since Lia's +run-once returns a pair of [yield accumulator], we grab the yield with =< to get the head (-) of the result. +yield-need is a Lia function that asserts that a yield is successful and returns the unwrapped result.

Below, we build Lia's +run-once gate and apply it to our imported .wasm-bin module, a pair of a noun accumulator and a map of imports, which will be just [~ ~], an empty jet hint %$ and finally the script itself.

Some more boilerplate. Hoon developers will recognize .m by analogy to the .m from the boilerplate often seen in threads. .arrows is our built +arrows core from Lia, and we expose that namespace with =, for convenient usage later.

We'll measure the input list and concatenate all of its elements into a single atom with +rep.

With that out of the way we can now interact with Wasm VM, replicating steps 1-4 of the JS binding we're using as a reference. We make heavy use of Hoon's ;< monadic pipeline builder, running expressions and piping the result directly into the one that follows.

Now we split the resulting octets atom ($octs, a cell of byte length and data) into a list of 64-bit atoms with +rip and add missing trailing zeroes if necessary.

Once you have the sort.wasm module and /gen/sort.hoon in your %base desk, run |commit %base and run this +sort generator in the Dojo; again we'll see the timed computation with %bout.

This is a ~10x speedup compared to the pure Hoon implementation.

Last updated