Looping Over Data

For loops allow one to iterate over sets of items. For example:

for person in people
  renderItem(person)
end

In this example, a new scope is created. The loop then iterates over all elements in people. For each element, a variable called person is assigned and the statement block that invokes renderItem is executed.

Nested Loops

You can also nest loops:

for person in people, brother in person.brothers
  renderItem(person, brother)
end

In this example, the outer loop iterates over all elements in people, assigning the identifier person to each element. For each person item, an inner loop is executed that iterates over the person's brothers property, assigning the identifier brother to each element. You'll notice that person is available in the inner loop's scope and that both identifiers are available in the statement block.

Else Clauses

You can also define an else clause for those cases where the for loop finds no matches:

for person in people, brother in person.brothers
  renderItem(person, brother)
else
  "I got nothin'!"
end

Guards

This becomes important if you apply guards to your ranges:

for person in people where person.type = 'stooge',
    brother in person.brothers where brother.living
  renderItem(person, brother)
else
  "I got nothin'!"
end

Loop Expressions

A for loop can also be used as an expression. With the exception of else clauses and statement blocks, all aspects of for loops are supported, even nested ranges!

import io

let colors = ['red', 'orange', 'yellow', 'green',
              'blue', 'indigo', 'violet']

let filtered = for c in colors where c != 'blue'
let moreFiltered = for c in filtered where c != 'orange'

for c in moreFiltered
  c | io.print
end

A for expression evaluates to a generator that produces a sequence of values or pairs. It's important to understand that the generator is stateful, meaning that once it has been exhausted further looping against that generator will yield no results.

Transforming

You can also transform the output by using a select clause.

import io
from string import title

let colors = ['red', 'orange', 'yellow', 'green',
              'blue', 'indigo', 'violet']

let filtered = for c in colors where c != 'blue'
let transformed = for c in filtered select title(c)

for c in transformed
  c | io.print
end

Loop Expressions As Statements

Because the select clause is only part of the expression form of for, it can be used to avoid confusing the parser when a for expression is used at the statement level. The other option would be to surround the expression in parentheses.

Array Comprehensions

Now take everything you've just learned about looping and let's turn that knowledge to the task of transforming lists. An Array Comprehension allows you to perform a for expression and transform the results into a new Array.

This will iterate over the previous declared colors, selecting only those colors with a length of 6, and then converting them to title case:

import io
from string import title, length
let c6 = [for c in colors where length(c) = 6 select title(c)]
c6 | io.print  # ['Orange', 'Yellow', 'Indigo', 'Violet']

It's important to note that this only works for Array Literals with a single embedded for expression. Otherwise, Fate will create an Array of generators and perform no iteration over them.

Object Comprehensions

Even better, you can create Objects from Arrays and vice-versa:

from string import title, length
let c6 = {
  for c in colors where length(c) = 6
  select c + '_key': title(c)
}
# c6 is now {orange_key:'Orange', yellow_key:'Yellow', ...

Reducing Results

Because Fate doesn't support mutation, a structured mechanism is required to facilitate this behavior. It should still offer the same level of mutation safety. Fate accomplishes this through the reduce statement and expression. Reduce is a way to declare a for loop that is expected to mutate variables within its iterations. This is accomplished by identifying the variables that will be mutated and then re-assigning their values inside of the loop. For example:

import io

reduce sum = 0
for value in values
  let sum = sum + value
end

sum | io.print

In this case, sum will initialize with a value of zero and be increment through each iteration of the loop. After the statement completes, sum will contain the aggregated value. Multiple variables can be mutated within a reduce statement.

reduce sum = 0, count = 0
for value in values where value < 100
  let sum = sum + value
  let count = count + 1
end
let avg = sum / count

The expression form of reduce is similar, but only allows for one variable to be tracked, and assigning it occurs implicitly via the select clause. Also, it evaluates to the mutated value:

let sum = reduce x = 0 for value in values select x + value

The variable named x will not find its way out of this expression.

results matching ""

    No results matching ""