Not-so-simple types
Collections are types which are used to aggregate and organize a set of data. Ryan supports two collection types: lists and dictionaries (or maps).
Lists
Lists are a collection of values, sequentially ordered. Here are some lists for you:
[1, 2, 3] // Lists are a comma-separated sequence of values between brackets.
[1, "a", null] // You can mix and match the types however you want.
[] // An empty list is also a list
[
1,
2,
3, // Use forgiving commas for long lists
]
The two main operations on lists are concatenation (just like with strings) and index accessing. Concatenation is pretty straightforward:
[1, 2, 3] + ["a", "b", "c"] // -> [1, 2, 3, "a", "b", "c"]
Alternatively, like in some programming languages, Ryan allows you to easily compose lists using flatten expressions (the ...
syntax below):
let x = [4, 5, 6];
[1, 2, 3, ...x] // -> [1, 2, 3, 4, 5, 6]
This yields a similar effect to adding lists.
Index accessing is also easy: get the n-th element in the list. However, Ryan shares a pet-peeve with many other programming languages: the first position is indexed by the number zero.
[1, 2, 3][0] // -> 1
[1, 2, 3][1] // -> 2, not 1!
[1, 2, 3][3] // error! Tried to access index 3 of list of length 3
// (3 is the 4th element!!)
Dictionaries (or maps)
dictionaries are a collection of values indexed by strings. This name, dictionary, is quite apt in describing what it does. Just like the regular old book, it uniquely associates a word to a value. Here is an example of a Ryan dictionary:
{
"name": "Sir Lancelot of Camelot",
"quest": "to seek the Holly Grail",
"favorite_color": "blue"
}
However, the same dictionary is much nicer written this alternative way:
{
name: "Sir Lancelot of Camelot",
quest: "to seek the Holly Grail",
favorite_color: "blue", // use forgiving commas for extra niceness
}
Whenever the key of a dictionary could be a valid variable name, you can omit the double quotes of the string. This doesn't change the value of dictionary (both examples correspond to the same thing); this is just syntax sugar: an nicer way of expressing the same thing.
Dictionaries have other few different tricks on their sleeves. For example, it is syntactically valid to repeat a key in the dictionary:
{
a: 1,
a: 2,
}
However, only the last occurrence of the same key will count to the final result. The above dictionary evaluates to { a: 2 }
. You can also use the name of a variable directly, without the value, if the key coincides with the variable name:
let a = 2;
{
a, // ... as opposed to `a: a`
}
The above will also evaluate to { a: 2 }
.
You can also specify an if
guard at the end of each key, in order to make its insertion in the dictionary optional, like so:
{
a: 1,
b: 2 if "abc" == "def", // wrong!
c: 3 if 1 == 1, // quite true...
}
This will evaluate to { "a": 1, "c": 3 }
.
Lastly, just like with lists, you can concatenate dictionaries and index them in the very same fashion as you would do a list:
let x = { a: 1, b: 2, c: 3 };
let y = { d: 4, e: 5, f: 6 };
x + y // -> { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 }
x["a"] // -> 1
x["d"] // error! Key "d" missing in map
And you can also use flatten expressions with dictionaries, just as if you would do with a list:
let x = { a: 1, b: 2, c: 3 };
{
d: 4,
e: 5,
f: 6,
...x
} // -> { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 }
This can be useful when creating record inheritance structures in Ryan.
Lastly, if a certain key could be a valid variable name, one can also use the shorter .
operator to index dictionaries:
let x = { a: 1, b: 2, c: 3 };
x.a // -> 1
a.d // error! Key "d" missing in map