In SML, Boolean values are denoted directly, meaning the value “true” is denoted *true*, and the value “false” is denoted *false*. The set of Boolean values is referred to as the type *bool*. We say: The value *true* is a value of type *bool*. By this we mean that *true* is an element of the type (i.e., set) *bool*.

## Conditional Expressions

In programming languages, conditional expressions (and conditional statements) provide a very important mechanism for controlling the flow of execution in a program. In SML, a conditional expression has the form shown below.

*if e*

_{1}then e_{2}else e_{3}

In the conditional expression above (also known as an *if-then-else* expression), the words *if*, *then*, and *else* are keywords and *e _{1}*,

*e*,

_{2}*e*denote expressions.

_{3}In order for a conditional expression to be well-typed, the expression *e _{1}* must be of type

*bool*, and the expressions

*e*and

_{2}*e*must have the same type. The expression

_{3}*e*is referred to as the

_{1}*condition*of the conditional expression. The expression

*e*is referred to as the

_{2}*then-branch*of the conditional expression, and the expression

*e*is referred to as the

_{3}*else-branch*of the conditional expression. A well-formed conditional expression like the one shown above is evaluated as follows.

- The expression
*e*is reduced to its normal form. Since,_{1}*e*is of type_{1}*bool*, its normal form will be either*true*or*false*. - Case A:
*e*. In this case, the evalution of the conditional expression proceeds in the following manner._{1}⇒ true*if e*_{1}then e_{2}else e_{3}⇒ if true then e_{2}else e_{3}⇒ e_{2} - Case B:
*e*. In this case, the evalution of the conditional expression proceeds in the following manner._{1}⇒ false*if e*_{1}then e_{2}else e_{3}⇒ if false then e_{2}else e_{3}⇒ e_{3}

An important thing to notice about the evaluation of *if e _{1} then e_{2} else e_{3}* is that only either (1)

*e*and

_{1}*e*will be evaluated, or (2)

_{2}*e*and

_{1}*e*will be evaluated. In a single evaluation it will never be the case that

_{3}*e*,

_{1}*e*, and

_{2}*e*will all (three) be evaluated. This kind of selective evaluation is known as

_{3}*short-circuiting*. Though the syntax of conditional expressions may differ from one language to another, all mainstream programming languages short-circuit the evaluation of conditional-expressions in the manner described. To confirm that the evaluation of conditional expressions are indeed short-circuited, try evaluating the following conditional expressions.

- if true then 3 * 0 else 3 div 0
- if false then 3 div 0 else 3 * 0
- if true then 3 div 0 else 3 * 0

Note that the evaluation of the third expression demonstrates what happens when the computer tries to evaluate *3 div 0*. While not a proof, the results of this experiment are consistent with statement that the evaluation of conditional expressions is short-circuited in SML.

### Control Flow

An important aspect of conditional expressions is they provide a mechanism for controlling the flow of execution in a program. Consider the function*twoBricks* shown below whose formal parameter is a 3D point. The body of this function is a conditional expression that evaluates to a BLACK brick when the x value of the point it is applied to is even and a WHITE brick when x value is odd. Thus, in the body of the function *twoBricks* there are two different computational possibilities (or paths).

```
fun twoBricks (x,y,z) = if x mod 2 = 0 then BLACK
else WHITE
```

The table below shows the results of three different calls to the function *twoBricks*

Expression |
Evaluation Result |

twoBricks (0,0,0) | BLACK |

twoBricks (0,1,1) | BLACK |

twoBricks (1,0,0) | WHITE |

Conditional expressions can be combined in various ways to produce code through which a number of computational paths are possible. Care needs to be taken in order to avoid constructing code that is overly complex. In the code shown below the body of the function *threeBricks* consists of a *nested conditional expression*. Specifically, the *then-branch* of the larger (outermost) conditional expression is itself another (nested) conditional expression.

```
fun threeBricks (x,y,z) =
if x mod 2 = 0 then
if x mod 4 = 0 then BLUE
else LIGHTBLUE
else RED
```

The table below shows the results of three different calls to the function *threeBricks*

Expression |
Evaluation Result |

threeBricks (2,0,0) | LIGHTBLUE |

threeBricks (4,1,1) | BLUE |

twoBricks (1,0,0) | RED |

In the Bricklayer program below, the function *threeBricks* is passed as a parameter to the function *traverseWithin*. The function *traverseWithin* is a Level 5 Bricklayer function that accepts three arguments: two points that define a rectangular prism, and a function *f* that when applied to a point will return a brick. When called, *traverseWithin* will traverse (or visit) all the points in the given rectangular prism. To each point (x,y,z) visited, *traverseWithin* will place at that point, the (brick) result of the function call *f(x,y,z)*.

In the example below, *traverseWithin* will traverse (in no particular order) over all the points in the set { (0,0,0), …, (8,0,8) }. A point (x,y,z) is processed by placing, in the virtual space, the brick returned from the function call *threeBricks(x,y,z)*. Note that in the resulting artifact, it visually appears that the blue horizontal line falls underneath the red vertical line. How would the conditional expression in the function *threeBricks* need to be changed so that the red line appears to fall underneath the blue line?

```
open Level_5;
fun threeBricks (x,y,z) =
if x mod 2 = 0 then
if z mod 4 = 0 then BLUE
else LIGHTBLUE
else RED;
build (9,1,9);
traverseWithin (0,0,0) (8,0,8) threeBricks;
show "control flow";
```

## Boolean Operators

In SML, there are three Boolean operators shown in the table below that we will consider to be primitive operators.

Operator |
SML Symbol |
Example |

disjunction | orelse | true orelse false = false |

conjunction | andalso | true andalso false = false |

negation | not | not true = false |

Technically speaking, *andalso* and *orelse* are *derived forms*. Essentially, this means that expressions involving these operators are a shorthand notation for other (computational equivalent) expressions. An initial part of executing an SML program involves a preprocessing stage where derived forms are transformed (i.e., translated) into their computationally equivalent counterparts. The rules for how expressions containing *andalso* and *orelse* operators are transformed are shown below.

Derived Form |
Computationally Equivalent Expression |

e_{1} orelse e_{2} |
if e_{1} then true else e_{2} |

e_{1} andalso e_{2} |
if e_{1} then e_{2} else false |

The *not* operator is simply a function that has been defined in SML.

## Relational Operators

SML has four relational operators: (1) less than (<), (2) less than or equal to (<=), (3) greater than (>), and (4) greater than or equal to (>=). SML also has two equality-based operators: (1) equal to (=), and (6) not equal to (<>). Collectively the relational operators and equality-based operators are called*comparison operators*. The comparison operators can be used to compare integers. The equality-based operators can be used to compare aggregate values (e.g., tuples and lists) constructed from integers.

Expression |
Result |

4 < 5 | true |

4 <> 5 | true |

(2,3) = (2,3) | true |

[3,4,5] = [3,4,5] | true |

## Precedence and Associativity

The table below gives the precedence and associativity for Boolean, comparison, and integer operators. Note that the precedence of *orelse* and *andalso* are ~2 and ~1 respectively. This is in contrast to mathematics where the logical operators *and* and *or* have the same precedence. A good rule of thumb is the following.

*When in doubt insert parenthesis to make explicit the order of evaluation desired.*

In SML, function application has the highest precedence. Since *not* is nothing more than an SML defined function, its application has the highest precedence.

Operator |
Precedence |
Associativity |

orelse | ~2 | left associative |

andalso | ~1 | left associative |

=, <>, <, >, <=, >= | 4 | left associative |

+, – | 6 | left associative |

*, div, mod | 7 | left associative |

not | highest | left associative |