Learn Citril in 10 minutes
Everything you need to start writing real Citril programs, in one sitting.
Follow along in the REPL (citril) or save each snippet to a
.citril file and run it.
Hello, world
A program is a list of statements. print writes a line;
strings can interpolate expressions with ${...}:
let name = "world"
print("Hello, ${name}!")
# Hello, world!
Comments start with # and run to the end of the line.
Values and variables
Declare a variable with let, then reassign it with =.
Citril has nil, booleans, integers and floats (kept distinct), strings, lists,
and maps:
let count = 3 # integer
let ratio = 1.5 # float
let ok = true # boolean
let nothing = nil # the absence of a value
let word = "citril" # string
count = count + 1 # reassignment (no 'let')
count += 10 # compound assignment: += -= *= /= //= %= ^=
print(count) # 14
Only nil and false are falsy; everything else
(including 0 and "") is truthy.
Functions
Functions are declared with function and closed with
end. They are first-class values — you can pass them around and
return them:
function add(a, b)
return a + b
end
print(add(2, 3)) # 5
Closures
A function remembers the variables in scope where it was created. That makes it easy to build little stateful helpers:
function make_counter(start)
let n = start
return function()
n += 1
return n
end
end
let next = make_counter(10)
print(next()) # 11
print(next()) # 12
Control flow
if uses then, optional elif/else,
and closes with end:
function classify(x)
if x > 0 then
return "positive"
elif x < 0 then
return "negative"
else
return "zero"
end
end
There are two for loops — a numeric range and a
for … in over a list — plus while. Use
break and continue inside any of them:
let total = 0
for i = 1, 5 do # 1 to 5 inclusive
total += i
end
print(total) # 15
for name in ["Ada", "Alan", "Grace"] do
print(name)
end
let n = 3
while n > 0 do
print("t-minus ${n}")
n -= 1
end
Lists
Lists are ordered and indexed from 0. Negative indices count
from the end:
let xs = [10, 20, 30]
print(xs[0]) # 10
print(xs[-1]) # 30
print(len(xs)) # 3
push(xs, 40) # xs is now [10, 20, 30, 40]
The higher-order built-ins map, filter, and
reduce take a function as their first argument:
let nums = [1, 2, 3, 4, 5]
let doubled = map(function(x) return x * 2 end, nums)
let evens = filter(function(x) return x % 2 == 0 end, nums)
let sum = reduce(function(a, b) return a + b end, nums, 0)
print(doubled) # [2, 4, 6, 8, 10]
print(evens) # [2, 4]
print(sum) # 15
Maps
Maps hold key/value pairs and keep their insertion order. Read and write
fields with either map.field or map["field"]:
let user = { name: "Ada", role: "pioneer", born: 1815 }
print(user.name) # Ada
user.role = "programmer"
print(user["role"]) # programmer
print(keys(user)) # ["name", "role", "born"]
Errors
Raise an error with error(...) and handle it with
try … catch … end. The caught value is whatever was thrown:
function safe_divide(a, b)
if b == 0 then
error("cannot divide by zero")
end
return a / b
end
try
print(safe_divide(10, 2)) # 5
print(safe_divide(10, 0)) # raises
catch e
print("caught: ${e}")
end
Where to next
- The Language guide covers every construct in detail.
- The Standard library lists all ~75 built-in functions.
- The Cookbook has ready-made snippets for common tasks.
- Want to script your own C++ app or game engine? See Embedding.