[go: up one dir, main page]

Skip to content
Cameron Purdy edited this page Apr 4, 2020 · 5 revisions

An if statement is a conditional statement; a conditional statement allows some portion of code to be executed depending on the value of a particular condition. Specifically, an if statement allows the conditional execution of a block of code, zero or one times, with an optional alternative block of code (or another if statement) to execute if and only if the first block of code is not executed.

The execution of an if statement begins with the evaluation of the ConditionList (one or more Condition), which yields a Boolean value, and that value controls whether the body of the statement will be executed. For a single Condition:

  • The Condition can be an expression. In this case, the expression must yield at least one value, and the first value must be of type Boolean, which will be used as the result of the Condition. Any additional values are silently discarded.
  • Alternatively, the Condition can use an OptionalDeclaration (or OptionalDeclarationList), which defines one or more assignments that result from an expression. The expression must yield at least two values, and the first value must be of type Boolean, which will be used as the result of the Condition.
  • In either case, if the Condition expression short-circuits, then False will be used as the result of the ConditionList.

For a ConditionList with more than one Condition, evaluation begins with the first Condition. If the Condition evaluates to False or short-circuits, then the evaluation of the ConditionList completes at that point with the value False. If the Condition evaluates to True, then evaluation proceeds to the next Condition; if every Condition, in turn, evaluates to True, then the ConditionList evaluates to True.

This simple example illustrates several of the possibilities discussed above, and uses a simple Boolean expression for each of Condition in the two if statements:

if (hot)
    {
    return "This porridge is too hot!";
    }
else if (cold)
    {
    return "This porridge is too cold!";
    }
else
    {
    return "This porridge is just right.";
    }

The OptionalDeclaration and OptionalDeclarationList are used when the expression yields one or more values that are necessary within the body of the if statement, as in this example, where the Boolean value yielded from an Iterator.next() call is consumed by (becomes the result of) the Condition, and the String value yielded from that same call is assigned to a new variable s, which can then be used in the “then” body of the if statement, but is not assigned in the else body of the if statement:

void printNext(Iterator<String> iter)
    {
    @Inject Console console;
    if (String s := iter.next())
        {
        console.println($"next string={s}");
        }
    else
        {
        console.println("iterator is empty");
        }
    }

The OptionalDeclaration and OptionalDeclarationList constructs is quite flexible. Consider the following interface FooBar, and two variables fb1 and fb2 of the FooBar type, where fb2 is also Nullable:

interface FooBar
    {
    (Boolean, String, Int, Int) foo();
    conditional (String, Int, Int) bar();
    }

FooBar  fb1;
FooBar? fb2;

The method foo() always returns four values, but the method bar() may return four values or a single False value, because it declares a conditional return (just like the Iterator in the previous example). For both foo() and bar(), the first returned value is of type Boolean, and so both foo() and bar() can be used in an if statement:

if (fb1.foo())
    {
    // do something ...
    }

if (fb1.bar())
    {
    // do something ...
    }

Furthermore, since each declares a total of four return values, the OptionalDeclarationList allows up to three assignments from each:

if ((String s, Int x, Int y) := fb1.foo())
    {
    console.println($"foo()=True, s={s}, x={x}, y={y}");
    }
else
    {
    console.println($"foo()=False, s={s}, x={x}, y={y}");
    }

However, in the case of a conditional return from bar(), the else clause does not have access to any of the return values beyond the first Boolean, because the additional three return values are conditional:

if ((String s, Int x, Int y) := fb1.bar())
    {
    console.println($"bar()=True, s={s}, x={x}, y={y}");
    }
else
    {
    // unlike the foo() example above, variables s, x, and y
    // are NOT definitely assigned here, because bar() has a
    // conditional return
    console.println("bar()=False");
    }

A short-circuiting expression will yield the same control flow as a False value for the Condition, so if the Condition can short-circuit, then the assignments implied by the MultipleOptionalDeclaration cannot be assumed to have occurred before the else clause:

// the postfix "?" operator can short-circuit
if ((String s, Int x, Int y) := fb2?.foo())
    {
    console.println($"foo()=True, s={s}, x={x}, y={y}");
    }
else
    {
    // unlike the foo() example above, variables s, x, and y
    // are NOT definitely assigned here, because the condition
    // may have short-circuited at the '?' operator
    console.println("foo()=False");
    }

The Condition and ConditionList constructions are also used in the while statement and the do statement.

The ConditionList is reachable if the if statement is reachable; the ConditionList completes if it is reachable and either completes or short-circuits. The “then” statement block is reachable if the ConditionList completes and the value of the ConditionList is not the constant False. The else clause is reachable if the ConditionList completes, and the value of the ConditionList is not the constant True; in the case that the else clause is not present, then the else clause is assumed to be reachable and assumed to complete if an else clause with an empty statement block would be reachable and would complete. The if statement completes if the “then” statement block completes or if the else clause completes. If either the “then” statement block or the else clause (if present) is unreachable because the ConditionList is the constant value False or True, that unreachability is not considered to be an error. For example:

if (False)
    {
    // this code is unreachable, which would normally be a
    // compile-time error, but in order to preserve historical
    // anachronisms, this unreachable code is permitted
    foo();
    }

Definite assignment rules:

  • The VAS before the ConditionList is the VAS before the if statement.
  • The VAS before the “then” statement block is the VAST after the ConditionList.
  • If the ConditionList can short-circuit, then the VAS at each possible point of short-circuiting is joined with the VASF after the ConditionList before it is provided to the else clause.
  • The VAS before the else clause is the VASF after the ConditionList.
  • In the case that the else clause is not present, then the VAS after the else clause is the VAS before the else clause.
  • The VAS after the if statement is the VAS after the “then” statement block joined with the VAS after the else clause.

The if statement provides a local variable scope for any declarations in the ConditionList; that scope exists to the end of the if statement, which is to say that both the “then” statement block and the else clause are nested within that scope. The “then” statement block and the optional else statement block each naturally provide a local variable scope, because they are statement blocks.

    IfStatement:
        if ( ConditionList ) StatementBlock ElseStatementopt

    ElseStatement:
        else IfStatement
        else StatementBlock

    ConditionList:
        Condition
        ConditionList , Condition

    Condition:
        Expression
        OptionalDeclaration ConditionalAssignmentOp Expression
        ( OptionalDeclarationList , OptionalDeclaration ) ConditionalAssignmentOp Expression

    ConditionalAssignmentOp:
        :=
        ?=

And from VariableStatement:

    OptionalDeclarationList:
        OptionalDeclaration
        OptionalDeclarationList , OptionalDeclaration

    OptionalDeclaration:
        Assignable
        VariableTypeExpression Name

    VariableTypeExpression:
        val
        var
        TypeExpression

    Assignable:
        Name
        TernaryExpression . Name
        TernaryExpression ArrayIndexes