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