Grain is a functional programming language that compiles directly to WebAssembly. It brings ML-family type safety, pattern matching, and algebraic data types to the Wasm ecosystem.
Why Grain Matters
Most languages compile to Wasm as an afterthought. Grain was built FOR WebAssembly from day one — its type system, garbage collector, and runtime are all designed for the Wasm execution model.
What you get for free:
- Compiles directly to WebAssembly
- ML-family type system with inference
- Pattern matching and algebraic types
- First-class functions and closures
- Built-in Wasm GC integration
- Runs in browsers, Node.js, Wasmtime, or any Wasm runtime
Quick Start
# Install
npm install -g @grain/cli
# Create and run
grain compile hello.gr
wasmtime hello.gr.wasm
# Or run directly
grain run hello.gr
The Basics
module Main
let greeting = "Hello, Grain!"
print(greeting)
// Functions with type inference
let add = (a, b) => a + b
let result = add(3, 4)
print(toString(result)) // 7
// Pattern matching
let describe = (n) => match (n) {
0 => "zero",
1 => "one",
n when n < 0 => "negative",
_ => "many",
}
print(describe(42)) // "many"
Algebraic Data Types
module Shapes
enum Shape {
Circle(Number),
Rectangle(Number, Number),
Triangle(Number, Number, Number),
}
let area = (shape) => match (shape) {
Circle(r) => 3.14159 * r * r,
Rectangle(w, h) => w * h,
Triangle(a, b, c) => {
let s = (a + b + c) / 2
Number.sqrt(s * (s - a) * (s - b) * (s - c))
},
}
let shapes = [Circle(5), Rectangle(3, 4), Triangle(3, 4, 5)]
List.forEach((s) => print(area(s)), shapes)
Option and Result Types
module SafeCode
enum Option<a> {
Some(a),
None,
}
enum Result<a, b> {
Ok(a),
Err(b),
}
let findUser = (id) => {
if (id > 0) {
Ok({ name: "Alice", id: id })
} else {
Err("Invalid user ID")
}
}
match (findUser(42)) {
Ok(user) => print("Found: " ++ user.name),
Err(msg) => print("Error: " ++ msg),
}
Lists and Higher-Order Functions
module ListOps
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// Filter, map, reduce
let result = numbers
|> List.filter((n) => n > 3)
|> List.map((n) => n * 2)
|> List.reduce((acc, n) => acc + n, 0)
print(toString(result)) // 70
// List comprehension-style
let pairs = List.flatMap(
(x) => List.map((y) => (x, y), [1, 2, 3]),
["a", "b"]
)
Records
module Records
record User {
name: String,
age: Number,
email: String,
}
let createUser = (name, age, email) => {
{ name, age, email }
}
let greetUser = (user) => {
"Hello, " ++ user.name ++ "! You are " ++ toString(user.age)
}
let alice = createUser("Alice", 30, "alice@example.com")
print(greetUser(alice))
Useful Links
Building WebAssembly applications? Check out my developer tools on Apify for ready-made web scrapers, or email spinov001@gmail.com for custom solutions.
Top comments (0)