if and match are control structures that determine which code block to perform based on the evaluated result of one or more conditional expressions.

if vs. match

Let's use a simple number comparison to illustrate how if and match work.

if

In this heavily commented example, the code block to perform is determined by comparing a combined roll of two die with 7:

// Is roll equal to 7?
if roll == 7
  // If so, assign result to "Perfect"
  result = "Perfect"

// Otherwise ("else if"), is roll less than 7?
elif roll < 7
  // If so, assign result to "Too low!"
  result = "Too low!"

// Otherwise (since the roll is not 7 nor less than 7)
else
  // Assign result to "Too high!"
  result = "Too high!"

Every if structure follows these rules:

Since if may be used as an expression, the above code can be simplified to:

result = if roll == 7
  "Perfect"
elif roll < 7
  "Too low!"
else
  "Too high!"

If a block has multiple statements, the expression in the last statement of the block is used as the value of that block.

The above example can also be written on a single line:

result = if roll == 7 {"Perfect"} elif roll<7 {"Too low!"} else {"Too high!"}

if clause

When an if structure has only a single block containing only a single statement, this can be written on a single line by appending an if clause to the statement:

bignumber = true if nbr > 100

That line is exactly equivalent to:

if nbr > 100
  bignumber = true

match

The match statement works similarly to if. Its purpose is to match a single value against a list of patterns. In effect, it splits up a conditional expression into two parts: the value to match and a list of patterns to match it against using the ~~ operator.

Here is the earlier example rewritten using match:

result = match roll
with 7
  "Perfect"
with 1 .. 6    // Using a range of values as a match pattern
  "Too low!"
else
  "Too high!"

match has a number of other tricks up its sleeves, described in depth later in this chapter once we have described the powerful match operator.

Conditional Expressions

A wide variety of conditional expressions can be formulated, from simple equivalence or comparisons to complex algorithms that use logical operators, pattern matching, functions or methods.

True vs. False

A conditional expression evaluates to true or false. The integer number 0, the null pointer, and false all evaluate to false. All other values evaluate to true. Thus:

// This statement will never execute
a += 1 if 0   // since 0 is equivalent to false

// This statement will always execute
a += 1 if "hello"

These rules make it possible to use function or method calls as part of a conditional expression:

a += 1 if a.isOdd

Conditional expressions are not just for if and while. A conditional expression may be used within any expression:

imm isOne Bool = (a == 1)    // isOne will be either true or false

Equivalence Operators

The == operator returns true if two values have the same content and type. != (not equal) is true if they do not. For example:

x == 3.0        // Returns true if the local variable x's value is 3.0
x+1 != y        // Returns true if x is not exactly 1 greater than y
true != false   // Returns true

As with the arithmetic operators, the == and != operators are implemented as type-specific methods. Thus, each type can establish its own rules for equivalence. For example, two floating point numbers could be evaluated as equal if they are within some margin of error (resulting from floating point calculation rounding errors). Similarly, two lists might be equal if they have the exact same contents. Some types may not implement the == method.

Comparison Operators

Comparison operators determine whether one value is greater or less than some other value. They work only on types that are comparable, such as numbers.

x >= 2          // Returns true if x is greater than or equal to 2 (>, <, <= are variants)
2 <= 2.0        // Returns false any time the types do not agree

Float values are compared using the same closeness criteria described above. For one Float value to be less than another, it must be less by at least the comparable closeness.

In some cases, a type can make its values comparable by implementing just the <=> (rocket-ship) operator/method. This returns an integer (rather than true or false): -1 if less, 0 if equal, and 1 if greater.

-2 <=> 4      // Returns -1

Use of the Comparable trait would then implement the other comparison operators using the rocket-ship operator.

Pattern Matching Operator

The ~~ pattern match operator is used to match a value to a specific pattern. For example:

5 ~~ i32      // true, since the type of 5 is i32
5 ~~ 1 .. 7   // true, since 5 is within the range between 1 and 7

The ~~ operator is powerful and unusual:

Use of the powerful ~~ operator is made possible by the richness of pattern-matching types it is capable of supporting: set membership, regular expression, search selectors (e.g., for parts), parsers, input decoder, translater, information analysis, etc.

Boolean Operators

Acorn has three boolean operators: and ('&&'), or ('||') and not ('!'). One can use either the word or the symbol. For example:

0==3 or not 2<3 and 3==3   // Returns true ('and' is evaluated before 'or')
0==3 || !2<3 && 3==3       // equivalent to above

The logical operators work mostly how you would expect:

Note: In case it is not obvious by now, these boolean operators are not the same as the boolean bit-wise logical operators described earlier.

Note 2: If a comparison operator or boolean operator is used outside the context of a control structure that wants a conditional expression (e.g., if), in most cases it will evaluate to a value of 'true' or 'false'. If the conditional expression just uses 'and' or 'or' operators, its value will be the last value evaluated in that expression.

Match statement

Now that we have talked about the ~~ pattern match operator, let's return to the match statement which makes use of that operator. By default, the 'match' statement uses the '~~' method to match each successive pattern against the value of the 'match' statement's value.

The 'using' clause

Append a 'using' clause to the 'match' statement to specify a specific method to use for matching (as opposed to using the default '~~'):

match number using `==`
with 0
	.turn
with 1
	.jump

The using clause can specify any method name or an executable function or closure.

Multiple patterns on a 'when'

A 'with' statement can list multiple match patterns, separated by commas. A match on any of those match patterns activates that code block.

match number using '=='
with 0,1
	.turn
with 2,3,4
	.jump
Extracting match results

Some patterns do more than matching. They also extract or transform matched elements within a successfully matched source value, returning one or more new digested value (so long as the first returned value is not false or null, which indicates a match failure). Specify the variables names that hold these returned values with an 'into' clause at the end of the 'match' statement:

match command into thing
with +Regex"fight [*]"
	combat: true
	opponent: thing
with +Regex"use [*]"
	.use(thing)

Regex returns the portion of the Text that lies within the square brackets. So, if command was "fight Lux", the first match would succeed and thing would be set to "Lux".

Alternatively, the return variable name(s) may be specified on any specific 'with' statement:

match command
with +Regex"fight [*]" into npc
	combat: true
	opponent: npc
with +Regex"use [*]" into item
	.use(item)

_