Skip to content

Syntax Basics

A Kiwi program is a sequence of statements, usually one per line. There are no semicolons. Statements run from top to bottom; there is no required entry point.

Blocks start with a keyword and close with end. Indentation is not significant, but two spaces is the convention.

fn classify(n)
if n % 2 == 0
"even"
else
"odd"
end
end
for i in [1 to 3] do
println "${i} is ${classify(i)}"
end

Loops (for, while, repeat) and lambdas open their body with do. Conditionals, functions, structs, packages and try don’t.

A newline ends a statement unless the expression is clearly unfinished: an open bracket, parenthesis or brace, or a binary operator at the end of the line or the start of the next one.

total = 1 +
2 +
3
items = [
"kiwi",
"mango",
]
println total # 6
println items # ["kiwi", "mango"]

A binary operator can also start the continuation line:

ready = true
loaded = true
ok = ready
&& loaded
&& !false
println ok # true

The exception is -: a line that starts with - is a new statement (a negative number), so put a subtraction’s - at the end of the line.

Method chains can continue on the next line by starting it with .:

result = [1 to 10]
.filter(do (n) => n % 2 == 0)
.map(do (n) => n * 10)
println result # [20, 40, 60, 80, 100]

A [ at the start of a line always begins a new list literal. It is never an index into the expression on the line above.

# starts a comment that runs to the end of the line. /# and #/ delimit a block comment, which can span lines or sit in the middle of one.

# A line comment.
x = 5 # A trailing comment.
/#
A block comment.
It can span many lines.
#/
println "a" /# inline #/ + "b" # ab

Block comments placed directly above a declaration are docstrings.

Names start with a letter or underscore, followed by letters, digits or underscores. They are case sensitive. The conventions are:

KindConventionExample
Variables, functions, methodssnake_caseuser_count, load_config
Structs, enumsPascalCaseHttpResponse, Color
ConstantsUPPER_SNAKE_CASE (required)MAX_RETRIES
Private helpersleading __parse_header
Packageslower casejson, my_app::utils

Kiwi has no ?-suffixed predicate methods (empty?, has_key?). A ? is always parsed as the ternary operator, and a stray one produces confusing syntax errors on the following lines.

Keywords can’t be used as names. That includes the type names integer, float, boolean, string, list, hashmap, lambda, object, date, bytes, pointer, generator, any and none. Using one as a variable, parameter or function name is a syntax error that suggests an alternative:

list = [1, 2, 3]
# SyntaxError: `list` is a type name and can't be used as a variable name. Rename it, e.g. `items`.

The same applies to bare hashmap keys and member access. Quote the key instead:

counts = {"list": 1, "string": 2}
println counts["list"] # 1
println 42 # integer
println -7 # integer
println 0xFF # integer, hexadecimal: 255
println 0b1010 # integer, binary: 10
println 0o17 # integer, octal: 15
println 3.14 # float
println 1.5e3 # float: 1500
println true # boolean
println null # null
println "text" # string with interpolation and escapes
println 'raw' # raw string
println [1, 2] # list
println {a: 1} # hashmap
println b"hi" # bytes: [104, 105]

Strings are covered in Strings, lists in Lists, and hashmaps in Hashmaps.

Integers are 64-bit. A hexadecimal, binary or octal literal is a 64-bit pattern, so 0xFFFFFFFF is 4294967295 and 0xFFFFFFFFFFFFFFFF is -1. A literal that doesn’t fit in 64 bits is an error.

To call a method on a number literal, wrap the literal in parentheses: (42).to_string(), (3.7).to_integer().

... is a statement that does nothing. Use it for bodies you haven’t written yet.

fn todo()
...
end
println todo() # null

A block comment directly above a package, struct, function or constant documents it. The standard library uses @-tags, and the standard library reference is generated from them.

/#
@summary Converts a temperature from Celsius to Fahrenheit.
@param float celsius : Degrees Celsius.
@return float : Degrees Fahrenheit.
@example
---
println to_fahrenheit(100.0) # 212
---
#/
fn to_fahrenheit(celsius: float): float
celsius * 9 / 5 + 32
end
TagFormatUse
@summary@summary TextOne-sentence description. Every public item should have one.
@param@param type name : DescriptionOne per parameter, in order. Use any for dynamic types.
@return@return type : DescriptionWhat the function returns.
@throws@throws ErrorType : DescriptionErrors the function can throw.
@exampleCode between --- linesA short, runnable example.
@see@see name : NoteRelated items.
@deprecated@deprecated : Use x insteadMarks an item as obsolete.

Keep summaries about what and why; the signature already says how.

eval parses and runs a string of Kiwi code and returns the value of its last expression.

eval "println 1 + 2" # 3
println eval "3 * 3" # 9