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 results
  • any allows one to await a single element of an array
  • all allows one to await all elements of an array
  • def defines a new Function
  • return returns a result from a Function
  • o composes two Functions
  • do starts an asynchronous block of statements
  • when asks a do block to wait until assignments are resolved
  • case ask a do branch to execute depending on first resolution
  • for identifies ranges over which to loop
  • reduce reduces an array or object into a set of results
  • where specifies a guard clause in a Function or range
  • select synthesizes a value in a comprehension or reduction
  • import identifies a module or its elements for importing
  • from it used to identify a module in an import statement
  • export makes variables available outside of this module
  • as is used to perform aliasing in various contexts
  • let is used to assign values
  • like matches an expression to a Pattern
  • match executes a block depending on a matched expression
  • in determines if an element is in an Array
  • and is a logical AND operator
  • or is a logical OR operator
  • mod is the remainder of an integer division
  • not is the logical NOT operator
  • if is used for logical branching
  • unless is used for logical branching
  • else creates an alternative logical branch
  • end 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 true
  • false is the literal boolean value false
  • self identifies the current Function invocation
  • it identifies the current Pattern context
  • global 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

results matching ""

    No results matching ""