Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

HWML Language Features

This guide walks through the main features of HWML using working examples. Every code snippet here is typechecked by the compiler, so you can trust that the syntax is correct.

Basic Definitions

Most HWML programs start by defining values. The def keyword introduces a new definition.

prim Bit : Signal

def id (x : Bit) : Bit := x

Type annotations are optional when the compiler can infer them.

prim Bit : Signal

def zero : Bit := 0
def one := 1

Functions

Functions can take multiple parameters. You can write them with or without type annotations.

prim Bit : Signal

def and_gate (a : Bit) (b : Bit) : Bit := a
def or_gate a b := a

Lambda syntax works too.

prim Bit : Signal

def not_gate : Bit -> Bit := fun x => x
def xor_gate := fun a b => a

Primitives

The prim keyword declares external types and values that the compiler knows about but doesn’t define itself.

prim Bit : Signal

def bit_zero : Bit := 0
def bit_one : Bit := 1

Function Types

Function types use the arrow ->. HWML supports dependent types, where the return type can depend on the value of a parameter.

prim Bit : Signal

def identity_type := Bit -> Bit
def gate_type := Bit -> Bit -> Bit
def dependent_fn (x : Bit) : Bit -> Bit := fun y => y

Hardware Arrow

The fat arrow => distinguishes hardware module types from regular mathematical functions. This matters because hardware modules represent synthesizable circuits, not just computations.

prim Bit : Signal

def hw_identity_type := Bit => Bit
def hw_gate_type := Bit => Bit => Bit

Pattern Matching

Pattern matching uses the match keyword. Each branch starts with a pipe and ends with an arrow pointing to the result.

prim Bit : Signal

def not_bit (b : Bit) : Bit :=
  match b with
  | 0 => 1
  | 1 => 0
  end

Meta Definitions

Meta definitions run at compile time. They’re useful for generating code or computing types.

prim Bit : Signal

meta bit_type := Bit
meta make_identity (t : _) := fun (x : t) => x

Let Expressions

Local bindings use let ... in syntax. Each binding is visible in the expressions that follow.

prim Bit : Signal

def example (x : Bit) : Bit :=
  let y := x in
  let z := y in
  z

Application

Function application is just whitespace. Write the function followed by its arguments.

prim Bit : Signal

def identity (x : Bit) : Bit := x
def result := identity 0

def and_fn (a : Bit) (b : Bit) : Bit := a
def and_result := and_fn 0 1

Grouping

Parentheses control precedence and grouping.

prim Bit : Signal

def nested := (fun x => x) 0
def fn_type := (Bit -> Bit) -> Bit

Parameter Styles

You can mix typed and untyped parameters however you want.

prim Bit : Signal

def fn1 a b c := a
def fn2 (a : Bit) (b : Bit) (c : Bit) : Bit := a
def fn3 (a : Bit) b (c : Bit) := a

Examples

prim Bit : Signal

def id (x : Bit) : Bit := x
prim Bit : Signal

def compose (f : Bit -> Bit) (g : Bit -> Bit) (x : Bit) : Bit :=
  f (g x)
prim Bit : Signal

def const_zero : Bit := 0
def const_one : Bit := 1
def always_zero (x : Bit) : Bit := const_zero