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#
| Type | Description |
|---|---|
Int | 64-bit signed integer |
String | UTF-8 string |
Bytes | Raw byte sequence |
Unit | Unit type (()) |
Bool | true / false |
Tuple | Dynamically-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.