WIT type mappings
wit-bindgen-scala maps WIT types in your world to Scala types. Generated imports live under {base-package}.{namespace}.{package}; exports under {base-package}.exports.{namespace}.{package}.
Scalar and composite types
| WIT | Scala |
|---|---|
bool | Boolean |
s8 | Byte |
u8 | scala.scalajs.wit.unsigned.UByte |
s16 | Short |
u16 | scala.scalajs.wit.unsigned.UShort |
s32 | Int |
u32 | scala.scalajs.wit.unsigned.UInt |
s64 | Long |
u64 | scala.scalajs.wit.unsigned.ULong |
f32 | Float |
f64 | Double |
char | Char |
string | String |
list<T> | Array[T] |
option<T> | scala.scalajs.wit.Option[T] |
result<T, E> | scala.scalajs.wit.Result[T, E] |
tuple<T1, T2> | scala.scalajs.wit.Tuple2[T1, T2] |
Unsigned integers use scala.scalajs.wit.unsigned so they can be distinct from signed Scala numeric types at the ABI boundary.
User-defined types
| WIT | Scala |
|---|---|
record | final class with @WitRecord |
variant | sealed trait with @WitVariant, final class for cases |
enum | sealed trait with @WitEnum, object for cases |
flags | final class wrapping Int |
resource (import) | opaque final class with @WitResourceImport |
Imports, exports, and resources
Import function
WIT (world scala imports this interface; see hello component model):
package scala-wasm:rust-compose;
interface greeter {
greet: func(name: string) -> string;
}
Generated Scala (in the greeter package object under managed sources):
import scala.scalajs.wit.annotation.{WitImport, WitScope, WitName}
import scala.scalajs.wit
@WitImport(
WitScope.unversioned("scala-wasm", "rust-compose", "greeter"), "greet")
def greet(
@WitName("name") name: String): String = wit.native
Export function
Same greeter interface, but your world exports it.
WIT:
world my-server {
export greeter;
}
Stub (scope matches your package and interface):
import scala.scalajs.wit.annotation.{WitExport, WitScope, WitName}
import scala.scalajs.wit
object GreeterExport {
@WitExport(
WitScope.unversioned("scala-wasm", "rust-compose", "greeter"), "greet")
def greet(@WitName("name") name: String): String = s"Hello, $name!"
}
Imported resource
WIT:
interface counters {
resource counter {
constructor(initial: s32);
increment: func();
get: func() -> s32;
}
}
Generated Scala:
@WitResourceImport(scope, "counter")
final class Counter private () extends Object {
@WitResourceMethod("increment")
def increment(): Unit = scala.scalajs.wit.native
@WitResourceMethod("get")
def get(): Int = scala.scalajs.wit.native
@WitResourceDrop
def close(): Unit = scala.scalajs.wit.native
}
object Counter {
@WitResourceConstructor
def apply(initial: Int): Counter = scala.scalajs.wit.native
}
Use val c = Counter(0); c.increment(); c.get(); c.close(). Resource exports are not generated.
Record, variant, and result
interface catalog {
record item {
sku: string,
tags: list<string>,
}
variant money { cash(u32), none }
enum status { ok, not-found }
lookup: func(sku: string) -> result<item, status>;
}
@WitRecord(scope, "item")
final class Item(
@WitName("sku") val sku: String,
@WitName("tags") val tags: Array[String])
@WitVariant(scope, "money")
sealed trait Money
object Money {
@WitName("cash")
final class Cash(@WitName("value") val value: scala.scalajs.wit.unsigned.UInt) extends Money
@WitName("none")
object None extends Money
}
@WitEnum(scope, "status")
sealed trait Status
object Status {
@WitName("ok") object Ok extends Status
@WitName("not-found") object NotFound extends Status
}
@WitImport(scope, "lookup")
def lookup(@WitName("sku") sku: String): scala.scalajs.wit.Result[Item, Status] =
scala.scalajs.wit.native
Naming
- Types:
kebab-case→PascalCase - Functions and fields:
kebab-case→camelCase - Packages:
kebab-case→snake_case1 - Scala keywords: backticks (e.g.
`type`)
Generator limits
- Resource exports are not supported (import only).
- Futures and streams are not supported yet.
-
I don't like it, this might change in the future. ↩