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:
await
awaits asynchronous resultsany
allows one toawait
a single element of an arrayall
allows one toawait
all elements of an arraydef
defines a new Functionreturn
returns a result from a Functiono
composes two Functionsdo
starts an asynchronous block of statementswhen
asks ado
block to wait until assignments are resolvedcase
ask ado
branch to execute depending on first resolutionfor
identifies ranges over which to loopreduce
reduces an array or object into a set of resultswhere
specifies a guard clause in a Function or rangeselect
synthesizes a value in a comprehension or reductionimport
identifies a module or its elements for importingfrom
it used to identify a module in animport
statementexport
makes variables available outside of this moduleas
is used to perform aliasing in various contextslet
is used to assign valueslike
matches an expression to a Patternmatch
executes a block depending on a matched expressionin
determines if an element is in an Arrayand
is a logical AND operatoror
is a logical OR operatormod
is the remainder of an integer divisionnot
is the logical NOT operatorif
is used for logical branchingunless
is used for logical branchingelse
creates an alternative logical branchend
terminates 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.
true
is the literal boolean value truefalse
is the literal boolean value falseself
identifies the current Function invocationit
identifies the current Pattern contextglobal
identifies 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