Effective ilusm
Writing clear, idiomatic ilusm code
This document gives tips for writing clear, idiomatic ilusm code. It augments the language specification and the Tour of ilusm with practical examples and best practices.
1. Embrace Brevity
Tip
ilusm is designed for conciseness. Use short names, skip unnecessary keywords, and let the language do the work.
# Verbose (not ilusm style)
# function calculate_sum(numbers_list):
# total_sum = 0
# for each number in numbers_list: ...
# Idiomatic ilusm
sum(xs) =
s = 0
x <- xs: s = s + x
s
2. Use Pipelines for Data Flow
Tip
The pipeline operator | makes data transformations readable. Use it instead of nested calls.
# Nested (harder to read)
result = trl.srt(trl.fil(trl.map(data, fn), pred))
# Pipeline (preferred)
result = data | map fn
| fil pred
| srt
3. Use def for Constants
Tip
Use def for values that should not change. This prevents accidental reassignment.
def max_r = 3
def tmo = 30
def url = "https://api.example.com"
# Mutable only when necessary
i = 0
whl i < max_r:
prn(i)
i = i + 1
4. Use Pattern Matching
Tip
Pattern matching with mat is clearer than chained if statements.
# Chained ifs (avoid)
# if x == 1: prn("one")
# if x == 2: prn("two")
# ...
# Pattern match (preferred)
mat x: 1 => "one" | 2 => "two" | 3 => "three" | _ => "other"
5. Destructure for Clarity
Tip
Destructuring makes it clear what you are extracting from complex data.
# Object destructuring
point = {x: 10, y: 20}
{x, y} = point
prn($"Location: ({x}, {y})")
# List destructuring
[a, b, c] = [1, 2, 3]
6. Write Descriptive Functions
Tip
Even with short names, functions can be clear. Use consistent abbreviations.
# Clear abbreviations sum(xs) = ... # sum a list dbl(x) = x + x # double a value sqr(x) = x * x # square a value # User identifiers are unrestricted in length validate_user(usr) = ... format_date(dt) = ...
7. Handle Errors Explicitly
Tip
Use try for operations that might fail. Always check the err field.
# try(f) calls a 0-arg function, catches errors
# returns {val, err} - err is nil on success
parse() = jsn.dec(raw)
r = try(parse)
if r.err: prn($"Parse failed: {r.err}")
| prn(r.val)
8. Pipeline-Ready Functions
Tip
Design functions to work well in pipelines: take the main data as the first argument.
# trl functions are pipeline-ready: data first
# trl.fil(lst, f), trl.map(lst, f)
# In a pipeline, stage names work without trl. prefix
result = data | fil \(x) x > 0
| map \(x) x * 2
| srt
9. Use Rest Parameters
# ...name collects remaining args into a list
log(msg, ...args) =
prn($"[log] {msg}")
arg <- args: prn($" - {arg}")
log("startup", "port=8080", "mode=dev")
10. Organize with Modules
Tip
Import what you need at the top. Use the standard library - it covers most common tasks.
use trl use txt use jsn use fs # Your code follows...
Common Patterns
CLI Tool
use os
use fs
path = os.arg(1)
if path == nil:
prn("Usage: ilusm-vm run tool.ilu <file>")
os.ext(1)
if fs.has(path):
data = fs.rd(path)
prn($"Read {len(data)} bytes from {path}")
| prn($"File not found: {path}")
HTTP Server
use web
use jsn
app = web.app()
app.get("/", \(req) "Welcome")
app.get("/api/users", \(req)
users = [{id: 1, nam: "Alice"}, {id: 2, nam: "Bob"}]
web.jsn(users)
)
app.run(":8080")
Data Processing
use fs
use jsn
use trl
use txt
data = jsn.dec(fs.rd("input.json"))
lines = data | fil \(r) r.active == tru
| map \(r) r.nam
| srt
line <- lines: prn(line)
Anti-Patterns
Avoid: Deep Nesting
# Hard to follow
if a:
if b:
if c:
do_something()
Instead: Use guard clauses or pattern matching.
Avoid: Long Parameter Lists
# Too many params f(a, b, c, d, e, g) = ...
Instead: Use an object parameter or rest parameters.