The Boa Programming Guide - Quantifiers

Quantifiers in Boa provide an easy way to specify loops with conditions. While these are just sugars in the language, they are much more concise and thus easier to write and maintain.

The general form of quantifiers is given below:

quantifier (var: type; condition) statement

The code above defines a loop. Inside the loop, the quantifier variable var is declared. There are three kinds of quantifier: foreach, exists, or ifall. The quantifier variable also defines a type (usually int). This variable is used inside the condition and determines if and how often the given statement executes.

The condition must be a bool statement. If it is not boolean, the condition is rewritten by the compiler to def(condition). This allows, for example, easily iterating over arrays.

Let's look at each kind of quantifier via some simple examples.

foreach Quantifiers

The foreach quantifier will evaluate the given condition for each possible value. If the condition evaluates to true, then the specified statement executes with the quantifier bound to the value that caused the condition to evaluate to true.

For example, what if we wanted to increment a counter each time a Project's declared programming_languages contains the string java? We might write the following statement:

foreach (i: int; match(`java`, p.programming_languages[i])) counts[p.programming_languages[i]] << 1;

This statement declares the quantifier variable i to be of type int. The loop generated from this code will loop over all values in the array p.programming_languages, binding i for each value and evaluating the condition. If the condition evaluates to true, then the value 1 is sent to the output table counts[p.programming_languages[i]]. Note that the value of i is bound inside the body of the statement.

For reference, a de-sugared form semantically equivilent to the above could be:

{ length := len(p.programming_languages); for (i := 0; i < length; i++) if (match(`java`, p.programming_languages[i])) { counts[p.programming_languages[i]] << 1; } }

As you can see, the compiler automatically infers the bounds of the loop based on the given condition. If for example the condition was more complex and the quantifier variable used to index more than one expression, the generated length variable would use the minimum value from each expression.

exists Quantifiers

The exists quantifier will evaluate the given condition for each possible value. If there exists a condition that evaluates to true for at least one value, then the specified statement executes with the quantifier bound to a deterministically chosen value that caused the condition to evaluate to true.

For example, what if instead of incrementing the counter for each of a Project's declared programming_languages that contains the string java, we only want to increment it once if at least one contains that string? We might write the following statement:

exists (i: int; match(`java`, p.programming_languages[i])) counts[p.programming_languages[i]] << 1;

This statement declares the quantifier variable i to be of type int. The loop generated from this code will loop over all values in the array p.programming_languages, binding i for each value and evaluating the condition. If the condition evaluates to true for at least one value of i, then the value 1 is sent to the output table counts[p.programming_languages[i]]. This value is sent exactly 0 or 1 time. Note that the value of i is bound inside the body of the statement to a deterministically chosen value that caused the condition to evaluate to true.

For reference, a de-sugared form semantically equivilent to the above could be:

{ length := len(p.programming_languages); i := 0; for (; i < length; i++) if (match(`java`, p.programming_languages[i])) break; if (i < length) { counts[p.programming_languages[i]] << 1; } }

Again, as you can see, the compiler automatically infers the bounds of the loop based on the given condition.

ifall Quantifiers

The third kind of quantifier is the ifall quantifier. This quantifier evaluates the given condition for each possible value. If the condition evaluates to true for all values, then the specified statement executes exactly once. The quantifier variable is not available in the body of the statement.

For example, perhaps we want to count all Revisions that only committed Java files. For this, we might write the following code:

ifall (i: int; hasfiletype(rev.files[i], "java")) counts << 1;

This statement declares the quantifier variable i to be of type int. The loop generated from this code will loop over all values in the array rev.files, binding i for each value and evaluating the condition. If the condition evaluates to true for all values of i, then the value 1 is sent to the output table counts. This value is sent exactly 0 or 1 time.

For reference, a de-sugared form semantically equivilent to the above could be:

{ length := len(rev.files); stop := false; for (i := 0; i < length; i++) if (!hasfiletype(rev.files[i], "java")) { stop = true; break; } if (!stop) { counts << 1; } }

Again note the compiler inferred the loop bounds automatically. The quantifier variable is not available in the body, as the value of i in this case would be outside the bounds of the array.