Language Guide

IRIS is a functional language with ML-like syntax that compiles to SemanticGraph and then to native x86-64 machine code.

Basics#

Let bindings#

let introduces a named value. let..in scopes it to an expression:

let x = 42 in x + 1   -- 43

let double n = n * 2

With a type annotation:

let double n : Int -> Int = n * 2

Functions#

Functions are defined with let. Multiple parameters are curried:

let add a b = a + b
let result = add 3 4   -- 7

Recursion#

Recursive functions require let rec. Mutually recursive functions use and:

let rec factorial n =
  if n <= 1 then 1
  else n * factorial (n - 1)

let rec even n = if n == 0 then true else odd (n - 1)
and odd n = if n == 0 then false else even (n - 1)

Comments#

-- This is a line comment

Types#

TypeDescription
Int64-bit signed integer
StringUTF-8 string
BytesRaw byte sequence
UnitUnit type (())
Booltrue / false
TupleDynamically-sized ordered collection

There is no separate list type. Tuples serve as both fixed-size records and variable-length sequences. All collection operations (fold, map, filter) work on tuples.

Type annotations are optional. Cost annotations declare asymptotic complexity:

let sum xs : Tuple -> Int [cost: Linear(xs)] = fold 0 (+) xs
let double n : Int -> Int [cost: Const(1)] = n * 2

Pattern matching#

Match expressions#

Match on integers, constructors, or tuples:

match expr with
  | 0 -> "zero"
  | 1 -> "one"
  | _ -> "other"

Algebraic data types#

Define sum types with type. Constructors start with an uppercase letter:

type Option = Some(Int) | None
type Color = Red | Green | Blue
type State = Idle | Running(Int) | Paused(Int) | Done(Int)

Destructure with match:

let unwrap_or x default_val =
    match x with
      | Some(v) -> v
      | None -> default_val

let step state =
    match state with
      | Idle -> Running 0
      | Running(n) -> if n >= 100 then Done n else Running (n + 1)
      | Paused(n) -> Running n
      | Done(n) -> Done n

Constructors are automatically bound as functions: Some 42, Running 0, None.

Parametric types#

type Option<T> = Some(T) | None
type Result<T, E> = Ok(T) | Err(E)

Tuple patterns#

match pair with
  | (a, b) -> a + b

match triple with
  | (x, _, z) -> x * z

Guard clauses#

let classify n =
  match n with
    | n when n > 0 -> "positive"
    | n when n < 0 -> "negative"
    | _ -> "zero"

Higher-order functions#

Lambdas#

Anonymous functions use \ syntax:

let add = \x y -> x + y
let inc = \x -> x + 1

Map, filter, fold#

These operate on tuples (the universal collection):

map (\x -> x * 2) (1, 2, 3, 4)          -- (2, 4, 6, 8)
filter (\x -> x > 2) (1, 2, 3, 4)       -- (3, 4)
fold 0 (\acc x -> acc + x) (1, 2, 3, 4) -- 10

fold is the primary iteration construct. It replaces loops:

-- Count elements
fold 0 (\acc _ -> acc + 1) xs

-- Build a new collection
fold () (\acc x -> list_append acc (f x)) xs

Partial application#

Functions are curried, so partial application works naturally:

let add5 = add_curried 5
let double_all = map_with (\x -> x * 2)

Pipe operator#

|> threads a value through a chain of functions:

xs |> filter (\x -> x > 0)
   |> map (\x -> x * 2)
   |> fold 0 (+)

Operators#

Arithmetic#

+, -, *, /, %

Also available: neg, abs, min, max, pow.

Comparison#

==, !=, <, >, <=, >=

Logic#

&&, ||, !

Both && and || are short-circuit.

Bitwise#

bitand, or, xor, not, shl, shr, rotl, rotr, popcount, clz

let masked = bitand value 0xff
let shifted = shr value 8

Strings#

Strings are UTF-8. Core operations:

let n = str_len "hello"                       -- 5
let c = char_at "hello" 0                     -- 104 (ASCII 'h')
let s = str_concat "hello" " world"           -- "hello world"
let sub = str_slice "hello world" 0 5         -- "hello"
let greeting = str_concat "Hello, " (str_concat name "!")

Convert between types:

let s = int_to_string 42                      -- "42"
let chars = str_chars "abc"                    -- tuple of char codes

Tuples and lists#

Tuples are the universal container. There is no separate list type.

Construction#

let point = (1, 2, 3)
let pair = ("hello", 42)       -- mixed types are fine
let empty = ()                 -- unit / empty tuple

Access and manipulation#

let x = point.0               -- 1 (positional access)
let y = point.1               -- 2

let n = list_len xs            -- length
let v = list_nth xs 0          -- element at index
let ys = list_append xs 99     -- append element
let zs = list_concat xs ys     -- concatenate two tuples
let first3 = list_take xs 3    -- first 3 elements
let rest = list_drop xs 2      -- drop first 2 elements
let xs = list_range 0 10       -- (0, 1, 2, ..., 9)

Control flow#

If/then/else#

if n <= 1 then 1
else n * factorial (n - 1)

if is an expression and always returns a value.

Fold as iteration#

fold replaces loops. Accumulate over any tuple:

-- Sum
fold 0 (+) xs

-- Build a string
fold "" (\acc x -> str_concat acc (int_to_string x)) xs

-- Match inside fold
fold 0 (\acc item ->
  match item with
    | Ok(v) -> acc + v
    | Err(_) -> acc
) results

For simple ranges, combine with list_range:

-- Sum 1 to 100
fold 0 (+) (list_range 1 101)

Imports#

Import a file by path, relative to the importing file:

import "stdlib/option.iris" as Option
import "stdlib/result.iris" as Result

All top-level let and type declarations become available. Constructors from imported ADT types are propagated automatically:

import "stdlib/option.iris" as Option

let x = Some 42          -- constructor in scope
let v = unwrap_or x 0    -- function in scope

Circular imports are detected at compile time.

I/O#

Print#

let _ = print "hello"
let _ = debug_print value

File I/O#

let contents = file_read "data.txt"

let h = file_open "data.txt" 0      -- 0 = read-only
let bytes = file_read_bytes h 4096
let _ = file_close h

Other effects#

TCP networking, environment variables, time, threading, and JIT compilation are available as built-in effects. See the full reference.