Lily's block syntax is a unique blend with influences from C, Python, Ruby, and Rust. The most important parts are:
No semicolons. Lily can work out automatically when one expression stopts and another starts. This allows expressions to span multiple lines.
Lily uses curly braces to denote the scope of a block. Unlike most curly brace languages, Lily uses one set of curly braces to cover a whole block. In practice, it looks like this:
Here's an example in C.
int abc, def, ghi, jkl;
if (a == 1) {
abc = 2;
def = 3;
}
else: {
ghi = 4;
jkl = 5;
}
The same example in Lily.
var a = 1, abc = 0, def = 0, ghi = 0, jkl = 0
if a == 1: {
abc = 2
def = 3
else:
ghi = 4
jkl = 5
}
Lily allows several types to be checked for truthiness. Here are the different types and what Lily considers false for them.
Type | False value |
---|---|
Boolean |
false :P |
Double |
0.0 |
Integer |
0 |
List |
[] |
String |
"" |
Conditional execution of code.
define letter_for_grade(grade: Integer): String
{
if grade >= 90: {
return "A"
elif grade >= 80:
return "B"
elif grade >= 70:
return "C"
elif grade >= 60:
return "D"
else:
return "F"
}
}
Custom scope for a block of code.
{
var v = 10
print(v) # 10
}
{
var v = "hello"
print(v) # hello
}
Execute a block of code as long as a condition is met.
{
var values: List[Integer] = []
var i = 0
do: {
values.push(i)
} while i
print(values) # [0]
}
{
var values: List[Integer] = []
var i = 0
# do supports break and continue
do: {
i += 1
if i % 2: {
continue
elif i == 4:
break
else:
values.push(i)
}
} while i < 5
print(values) # [2]
}
{
# Variables declared in a do cannot be used for the condition.
#[
values = []
i = 0
do: {
if 1: {
continue
}
var v = 10
} while v == 10
]#
}
Execute a block of code a set number of times.
{
var values: List[Integer] = []
# Default increment is 1
for i in 0...5: {
values.push(i)
}
# Syntax error, because i is restricted to the for loop's scope..
# print(i)
print(values) # [0, 1, 2, 3, 4, 5]
}
{
var values: List[Integer] = []
# An existing local can be used for the increment.
var index = 0
# A custom increment can be specified.
for index in 0...10 by 2: {
values.push(index)
}
print(values) # [0, 2, 4, 6, 8, 10]
print(index) # 10
}
{
# For loops support break and continue
var values: List[Integer] = []
for i in 0...10: {
if i % 2: {
continue
elif i == 6:
break
}
values.push(i)
}
print(values) # [0, 2, 4]
}
{
var values: List[Integer] = []
# Modifications to the index and end are overwritten.
var start = 0
var end = 5
for i in start...end: {
values.push(i)
end = start
i = start
}
print(values) # [0, 1, 2, 3, 4, 5]
}
Simplified for
that automatically unpacks List
elements.
var v = [1, 2, 3, 4]
var result: List[Integer] = []
foreach element in v: {
result.push(element * element)
}
print(result) # [1, 4, 9, 16]
Exhaustive selection on an enum or class.
{
var v = Some("body")
var message = ""
match v: {
# Decomposition creates 's' in this scope.
# All fields in the variant must be decomposed.
case Some(s):
# Decomposition creates 's' in this scope.
message = s
case None:
}
print(message) # body
#[
# Syntax error, because decomposition must name all vars.
match v: {
case Some(s):
}
]#
}
enum Color {
Red,
Blue,
Green,
RGB(Integer, Integer, Integer)
}
{
var color = RGB(0xFF, 0xCC, 0xDD)
# Use _ to skip decomposition.
match color: {
case RGB(_, blue, _):
var f = "Custom color (blue: {}).".format(blue)
print(f) # Custom color (blue: 204).
case Red:
case Blue:
case Green:
}
# Empty variants can be multi-matched.
match color: {
case RGB(_, blue, _):
case Red, Blue, Green:
}
}
scoped enum ScopedColor {
Red,
Blue,
Green,
RGB(Integer, Integer, Integer)
}
{
# When matching against a scoped enum, the scope must be provided.
var color = ScopedColor.RGB(0xff, 0xcc, 0xdd)
match color: {
case ScopedColor.RGB(_, _, _):
case ScopedColor.Red,
ScopedColor.Blue,
ScopedColor.Green:
}
}
class MyValueError(message: String, code: Integer) < ValueError(message)
{
public var @code = code
}
{
var v: Exception = MyValueError("asdf", 1234)
var code = 0
# Match against a class always decomposes to one var.
# Unlike try, match only works against the exact class.
match v: {
case ValueError(e):
code = 1
case MyValueError(e):
code = e.code
# An else case is required, but can be empty.
else:
}
print(code) # 1234
}
class GenericError[A](message: String) < Exception(message)
{}
{
#[
# Cannot match against a generic class.
var v: Exception = GenericError("")
match v: {
case GenericError(e):
else:
}
]#
}
{
var caught = false
try: {
1 / 0
except IndexError:
# This branch is ignored.
except DivisionByZeroError:
caught = true
}
print(caught) # true
}
{
# A parent class of the raised exception can also be given.
# Use "as" to capture the exception and use it.
var str = "a1"
var error_message = ""
try: {
str.parse_i().unwrap()
except Exception as e:
error_message = e.message
}
print(error_message) # unwrap called on None.
}
class MyValueError(message: String) < ValueError(message)
{ }
{
# Errors can be raised through raise.
# Subclass matching is allowed.
var message = ""
try: {
raise MyValueError("Hello")
except ValueError as e:
message = e.message
except DivisionByZeroError as e:
0 / 0
}
print(message) # Hello
}
# Custom errors are also possible.
class MyError(message: String, code: Integer) < Exception(message)
{
public var @code = code
}
{
var code = 1
try: {
raise MyError("Oh no", 100)
except MyError as e:
code = e.code
}
print(code) # 100
}
var values: List[Integer] = []
var i = 0
while i != 5: {
values.push(i)
i += 1
}
print(values) # [0, 1, 2, 3, 4]
values = []
i = 0
# While loops support break and continue.
while i != 5: {
if i % 2: {
i += 1
continue
elif i == 4:
break
else:
values.push(i)
i += 1
}
}
print(values) # [0, 2]
Match against a specific case.
var v = Some(1)
# Using match to extract a specific case.
match v: {
case Some(s):
print(s) # 1
case None:
}
# Using "with" instead.
with v as Some(s): {
print(s) # 1
}
# With allows else.
with v as None: {
else:
print(v) # Some(1)
}
When used against a class, an exact class instance must be used.
var error: Exception = IndexError("")
with error as Exception(e): {
# This will not be executed.
0 / 0
}