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.