Skip to main content

Control Flow

In Quarrel, control flow has a slightly different arrangement than some imperative languages. Instead of a leading keyword like if, switch, case, loop, while and others; Quarrel uses a binary/ternary operator format in several different ways.

Conditionals#

Quarrel's conditional operators work differently from many languages in that there is no leading keyword like if that begins the conditional. Instead, the symbol sits between (infixed) the expression that is the "condition" and some sequence of code that will run depending on the result of the condition.

  • @@ means "if leading expression results in true then..."
  • ~@ means "if leading expression results in false then..."
  • ?@ means "if leading expression results in ()! then..."
  • ~ means "not"

Chaining works because the expression returns the resulting truth value.

c .= no
c @@ p1 ~@ p2 # reduces to:
(c @@ p1) ~@ p2 # reduces to:
false ~@ p2 # reduces to:
p2!

You can use any combination of the three or just one of these operations. The expression on the right-hand side is a sequence that is executed only if the conditional operation is successful. Even though the righthand side is a sequence, you can avoid enclosing parentheses unless another operation has higher precedence.

Ambiguity

Quarrel has yet to face some of these ambiguity concerns but there may be some syntax "linting" to spot ambiguity and there may also be some burden on the programmer to understand the precedence of Quarrel operators.

Transforms#

Transforms provide the ability to traverse aggregate data. They are bidirectional so you can use the <- or -> operator and the action that takes place depends on the type of aggregate data that you are supplying from. Additionally, the kind of receiving data will indicate a particular action.

Transforms are reversible, meaning they can be applied from left-to-right (traditional) or right-to-left. These operations are peformed with the basic transform operators -> and <- respectively. The - side argument is considered the supplier and the receiver for the operation.

Each (For-like)#

One of the most common usages, is to iterate over a table and apply some sequence to each item. To create arbitary tables for just the effect, you can supply a range.

i .= 0
[0::2] -> "ISS00" & (i .= i + 1) #= ["ISS001", "ISS002", "ISS003"]
# or using the range directly and the _ operator
[1::3] -> "ISS00" & _ #= ["ISS001", "ISS002", "ISS003"]

It also works over maps which supplies two arguments to the receiver, the key and the value. Instead of using \1 and \2, it will look cleaner to just provide a pattern to the sequence (turn it into a routine).

[apples. 12, pigs. 3, chickens. 8] -> key, value =>
value .= value - (key == 'pigs' @@ 1 ~@ 4)
#= [apples. 8, pigs. 2, chickens. 4]

Select (Switch-like)#

To perform a switch-like structure, provide a Table of routines. The Table determines uniqueness of a function argument by it's pattern (no patterns may be identical).

doubleData .= [
Text? t => t & t
Number? t => t + t
]
["Hello", 12] -> doubleData #= ["HelloHello", 24]

The transform then takes each element of the provided data, determines if it matches any of the routine patterns, then calls that sequence with the data element.

Special Transforms#

There are several special transforms that provide similar faculty as the main transform but do something additional

Zip#

This operator takes two tables and creates a table of pairs from each. Additionally you can provide a routine or a table of routines which will be tested and the result will be "zipped" together with the argument from the first table to form the pairs. The result of this operation can then be fed to a general transform and the pairs will be given to the receiver. This allows you to combine two operations at a table-processing level to act on something from each of them concurrently.

pairs .= [3,2,1] +> ["a", "b", "c"] #= [[3,"a"], [2,"b"], [1,"a"]]

Filter#

This operator's receiver is called a predicate and it returns a truth value to determine if that element stays in the resulting table or not.

evenOnly .= [1::10] $> _ %% 2 #= [2,4,6,8,10]

Reduce#

This operator actually creates a partial-evaluated application of the receiver which can be supplied to another transform that receives a base value (righthand side). There are two versions that have a sometimes-important semantic difference.The >- version starts with the first argument and processes through each element, from left-to-right, The >>- version starts with the last argument and processed through each element, from right-to-left.

[0!:9] >- * -> 1 #= 362880
# supplicand (table) >- applicand -> base (accumulator)
# (((((((((1 * 9) * 8) * 7) * 6) * 5) * 4) * 3) * 2) * 1)
[-12.99, 4.22, -1.99, 3.48] >>- + -> 100 #= 92.72

These two examples don't really illustrate when you would need one fold over the other due to their simple nature, they can be evaluated either direction and arrive at the same result. However, in more complex examples using a sequence instead of a operator, order will matter. Use the mnemonic that > for "starts with first" and >> for "starts with last" to help remember the difference.

Empiricals#