Lists, text and namespaces
Lists
A list is an ordered sequence of values, written in square brackets.
nums = [10, 20, 30]
nums # => [10, 20, 30]
[] # => []
The brackets enclose an expression, usually a tuple, and the list contains its
items. Since tuples are flat, a tuple inside the brackets adds its items
directly, and Nothing adds nothing. A list inside a list stays a single item:
[1, (2, 3)] # => [1, 2, 3]
[1, (), 2] # => [1, 2]
[[1, 2], [3]] # => [[1, 2], [3]]
Getting items
Apply a list to a number to get the item at that position. Positions start
at 0. A position that is negative, fractional or past the end gives Nothing.
nums = [10, 20, 30]
nums 0 # => 10
nums 2 # => 30
nums 10 # => ()
nums (0, 2) # => (10, 30)
Combining and transforming
+ concatenates lists. The list namespace has functions
to measure, search and slice them.
nums = [10, 20, 30]
nums + [40] # => [10, 20, 30, 40]
list.size nums # => 3
list.find 20 nums # => 1
list.head 2 nums # => [10, 20]
list.reverse nums # => [30, 20, 10]
Lists are values: no operation changes a list. To process each item, turn the
list into a tuple with list.enum, map a function over it, and put the result
back in brackets:
prices = [5, 12, 20]
[list.enum prices => (p -> p * 2)] # => [10, 24, 40]
Text
Text values support + (concatenation) and comparison. Applying a text to a
number gives the character at that position, starting at 0. Positions count
Unicode characters, not bytes.
word = "swan"
word 0 # => "s"
word (1, 2) # => ("w", "a")
word 10 # => ()
"sw" + "an" == word # => TRUE
The text namespace has functions to measure, search,
slice, split and join text.
text.size "héllo" # => 5
text.upper "texl" # => "TEXL"
text.split "," "a,b,c" # => ("a", "b", "c")
Namespaces
A namespace is a set of named values, written in curly braces.
point = {x: 3, y: 4}
point # => {x: 3, y: 4}
The braces enclose an expression, which Swan evaluates in a new scope. Every name bound during that evaluation becomes a member of the namespace. The members can use the members defined before them, and the names around the namespace:
scale = 10
box = {width: 2, height: width * 3, area: width * height * scale}
box.area # => 120
You can bind members with : or =. Inside the braces, the separators are
just the comma operator, so any expression works, including definitions of
functions:
counter = {start = 1, next = n -> n + 1}
counter.next counter.start # => 2
Reading members
There are two ways to read a member:
ns.nameevaluatesnameinside the namespace. The right side can be any expression, and it can use all the members.ns "name"looks up the member called"name". The key is a text value, so it can be computed.
point = {x: 3, y: 4}
point.x # => 3
point.(x * y) # => 12
point "y" # => 4
key = "x"
point key # => 3
point "z" # => ()
The two differ for names that are not members. ns "name" only sees the
members of the namespace, while ns.name also sees the names around the place
where the namespace was created, like any expression evaluated inside it:
point = {x: 3, y: 4}
point "math" # => ()
type point.math # => "Namespace"
Reading a member of Nothing gives Nothing, so a chain of . stops safely
at the first missing link:
config = {}
config.server.port # => ()
Adding members
A namespace can gain members after it is created. Bind them inside the
namespace with .:
point = {x: 3}
point.(y = 4)
point # => {x: 3, y: 4}
Inspecting namespaces
The nspace namespace lists and counts the members:
point = {x: 3, y: 4}
nspace.enum point # => ("x", "y")
nspace.size point # => 2
Namespaces as modules
Namespaces group related functions, like the built-in math and text
namespaces:
geometry = {
square = side -> side * side,
circle = radius -> math.PI * radius ^ 2
}
geometry.square 3 # => 9