DEV Community

Alex Spinov
Alex Spinov

Posted on

Grain Has a Free API: A Functional Language That Compiles to WebAssembly

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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),
}
Enter fullscreen mode Exit fullscreen mode

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"]
)
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

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)