RoyalIcing / Orb

Write WebAssembly with Elixir
https://useorb.dev
BSD 3-Clause "New" or "Revised" License
198 stars 1 forks source link
compiler dsl elixir wasm webassembly

Orb logo

Orb: Write WebAssembly with Elixir

Docs | Examples

Livebook: Temperature Converter

Write WebAssembly with the power of Elixir as your compiler:

Status

Orb is alpha in active development. My aim is to refine the current feature set and complete a .wasm compiler (current it compiles to WebAssembly’s .wat text format) in order to get to beta.

Libraries

Installation

Add orb to your list of dependencies in mix.exs:

def deps do
  [
    {:orb, "~> 0.1.0"}
  ]
end

Example

defmodule CalculateMean do
  use Orb

  global do
    @tally 0
    @count 0
  end

  defw insert(n: I32) do
    @tally = @tally + n
    @count = @count + 1
  end

  defw calculate_mean(), I32 do
    @tally / @count
  end
end

This can be converted to WebAssembly text format (wat):

wat = Orb.to_wat(CalculateMean)
# """
# (module $CalculateMean
#   (global $count (mut i32) (i32.const 0))
#   (global $tally (mut i32) (i32.const 0))
#   (func $insert (export "insert") (param $element i32)
#     (i32.add (global.get $count) (i32.const 1))
#     (global.set $count)
#     (i32.add (global.get $tally) (local.get $element))
#     (global.set $tally)
#   )
#   (func $calculate_mean (export "calculate_mean") (result i32)
#     (i32.div_s (global.get $tally) (global.get $count))
#   )
# )
# """

Write this out as a .wat WebAssembly text file:

File.write!("example.wat", wat)

You can then compile this to a .wasm WebAssembly file using wat2wasm from the WebAssembly Binary Toolkit:

wat2wasm example.wat

Or you can execute it directly in Elixir with OrbWasmtime:

alias OrbWasmtime.Instance

# Run above example
inst = Instance.run(CalculateMean)
Instance.call(inst, :insert, 4)
Instance.call(inst, :insert, 5)
Instance.call(inst, :insert, 6)
assert Instance.call(inst, :calculate_mean) == 5

Note there is another excellent Elixir Wasmtime wrapper out there called Wasmex, you may want to check that out too.

Composing modules

You can compose modules together using Orb.include/1:

defmodule Math do
  use Orb

  defw square(n: I32), I32 do
    n * n
  end
end

defmodule SomeOtherModule do
  use Orb

  Orb.include(Math)

  defw magic(), I32 do
    Math.square(3)
  end
end

Use cases

Why WebAssembly?

Why develop Orb in Elixir?

Here are the reasons I chose to write Orb in Elixir.