The Core Syntax
This chapter provides a quick overview of Fate's syntax, particularly how values are defined and retrieved.
Reserved Words
Fate reserves the following identifiers as keywords:
awaitawaits asynchronous resultsanyallows one toawaita single element of an arrayallallows one toawaitall elements of an arraydefdefines a new Functionreturnreturns a result from a Functionocomposes two Functionsdostarts an asynchronous block of statementswhenasks adoblock to wait until assignments are resolvedcaseask adobranch to execute depending on first resolutionforidentifies ranges over which to loopreducereduces an array or object into a set of resultswherespecifies a guard clause in a Function or rangeselectsynthesizes a value in a comprehension or reductionimportidentifies a module or its elements for importingfromit used to identify a module in animportstatementexportmakes variables available outside of this moduleasis used to perform aliasing in various contextsletis used to assign valueslikematches an expression to a Patternmatchexecutes a block depending on a matched expressionindetermines if an element is in an Arrayandis a logical AND operatororis a logical OR operatormodis the remainder of an integer divisionnotis the logical NOT operatorifis used for logical branchingunlessis used for logical branchingelsecreates an alternative logical branchendterminates various blocks and branches
Attempting to assign or retrieve these keywords as local variables will result in parsing errors.
Reserved Identifiers
In addition to the previous reserved keywords, Fate also reserves the following identifiers. Attempting to assign these keywords as local variables will result in parsing errors.
trueis the literal boolean value truefalseis the literal boolean value falseselfidentifies the current Function invocationitidentifies the current Pattern contextglobalidentifies the global object
Literals
Literals are values expressed in terms of themselves, rather than by variable references. So for example, if I talked about a variable name I would really be talking about whatever it is that name refers to. In the case of Literals, they are the values. Some might refer to literals as fixed, or atomic. Examples of Literals might include: 3.14, 'Hello, World', and false.
Numbers
Numeric Literals in Fate can only be represented as either real or integers, and only in decimal notation. The following are acceptable numeric literals:
0
103
99.995
19.123e12
5.32e-5
Strings
Strings are a series of characters (letters and so on). Fate provides two ways to represent strings: simple and multi-line.
A simple string starts and ends with either a single or double quote, and can not break across multiple lines:
'This is a simple string'
A multi-line string starts and ends with triple-quotes (''' or """) and may include line breaks:
'''
This
string
spans
multiple
lines
'''
Escaping
Strings allow special characters to be included using an escaping method (a leading backslash \). These characters are ones that would be difficult to represent as part of the string, such as a single quote or an embedded newline.
| Escape | Description |
|---|---|
| \\ | A single backslash |
| \" | A double quote |
| \' | A single quote |
| \b | Bell |
| \f | Form-Feed |
| \n | Unix Newline |
| \r | Carriage Return |
| \t | Tab |
Booleans
The value of a Boolean literal is either true or false, so that's how you write them: true or false.
Identifiers
An Identifier is a name that can be used to retrieve a variable or member. Fate Identifiers must start with one of the following characters: (a-zA-Z_$). All characters thereafter may also include digits: (0-9). Identifiers can not be any of the Fate reserved words.
Operators
Additive (+, -)
The additive operators are + and -.
Multiplicative (*, /, mod)
The multiplicative operators are *, /, and mod (modulo). The * can also be represented by the • character (Alt-8 on a Mac). The / can also be represented by the ÷ character (Alt-/ on a Mac).
Unary (-, not)
Only two traditional unary operators are supported. They are - for numeric negation, and not for boolean not negation.
-transactionAmount
not happy
Precedence Override
You can override the precedence by which expressions are evaluated by enclosing those expressions in parentheses ():
(28 - 7) * 2
Arrays
Arrays are a sequence of elements surrounded by square braces [] and separated by commas ,. The elements of an array can only be accessed by numerical index. These indexes are zero-based, meaning the first element is accessed with 0, and so on.
let a = [1 + 8] # single item array containing the number 9
let b = [5, 9 + 12] # two item array containing 5 and 21
a[0] # displays 9
b[1] # displays 21
Objects
Objects are a set of name/value pairs surrounded by curly braces {} and separated by commas ,. Both the names and values can be constructed using any valid expression. If the name is an Identifier, it will be treated as a literal string.
{
theMachine: 'Deep Thought',
theAnswer: (28 - 7) * 2
}
Member Retrieval
Like in JavaScript, membership expressions allow you to drill into an Array or Object's properties or elements.
myArray[0]
myArray[someIndex or 0]
myObject.someProperty
myObject['someProperty']
Assignment
Fate does not have a general-purpose assignment operator (like JavaScript's = operator). Instead, it allows you to bind variables to the current scope only using the let statement.
let a = 42, b = {name: 'Thom', age: a}, c = b | 'Howdy, %name'
You can also spread this across multiple lines:
let a = 42,
b = {name: 'Thom', age: a},
c = b | 'Howdy, %name'
Destructuring Assignment
Fate also supports Array and Object destructuring.
from io import print
let x = 10, y = 99
let [y, x] = [x, y]
let o = { name: 'Thom', age: 43 }
let {name, age} = o
# you can also draw out members using expressions or names
let {name as n, 'age' as a} = o
{n,a} | "%n is %a years old" | print
Additionally, one can use the wildcard (_) symbol in an array destructuring to ignore specific elements.
let [a, _, c] = [1, 2, 3] # 2 will be skipped
Static Single Assignment
One of the most important concepts to understand about Fate is that it compiles to Static Single Assignment Form. What this means is that if you re-assign a variable that's in scope, the language will not overwrite the data the variable points to. Instead, it will create an entirely new version of the variable, and leave the previous one intact.
Normally, you wouldn't even notice this behavior, but it becomes particularly important when you create nested functions and do blocks. In JavaScript, re-assigning a variable from within a closure means that you overwrite the variable for everyone referring to it. In Fate, this is not the case. For Example:
function outer() {
let x = 100;
function inner() {
x = x + 100;
}
inner();
return x; // will return 200
}
def outer
let x = 100
def inner
let x = x + 100
end
inner()
x # will return 100
end
The reason for this is because Fate treats your code as if it's something like this:
def outer
let x1 = 100
def inner
let x2 = x1 + 100
end
inner()
x1 # will return 100
end