Go to the first, previous, next, last section, table of contents.

Control Structures are Expressions

Scheme control structures are expressions, and return values. An if expression is a lot like a C if-then statement, but the "then" branch and the "else" branch are also expressions that return values; the if expression returns the value of whichever subexpression it evaluates.

For example,

(if (< a b)
    a
    b)

returns the value of either the variable a, or the variable b, whichever is less (or the value of b if they're equal). If you're familiar with ternary(1) expressions in C, this is like (a < b) ? a : b. In Scheme, there's no need for both an if statement and an if-like ternary expression operator, because if "statements" are expressions.

Note that even though every expression returns a value, not all values are used--you can ignore the return value of an if expression. The if special form can therefore be used to control what gets executed, or to return a value, or both. It's up to you.

The uniformity of value returning means that we never have to explicitly use a return statement, so Scheme doesn't have them. Suppose we wanted to write a function min to return the minimum of two numbers. In C, we might do it this way:

int min(int a, int b)
{
   if (a < b)
      return a;
   else
      return b;
}

In Scheme, we can just do this:

(define (min a b)
   (if (< a b)
       a
       b))

Whichever branch is taken, the value of the appropriate variable (a or b) will be returned as the value of that branch of the if, which is returned as the value of the whole if expression, and that is returned as the return value of the procedure call.

Of course, you can also write a one-branch if, with no "else" clause.

(if (some-test)
    (some-action))

The return value of a one-branch if is unspecified in the case the condition is false, so if you're interested in the return value, you should use a two-branch if, and explicitly specify what should be returned in both cases.

Notice that the flow of control is top-down, through the nesting of expressions---if controls which of its subexpressions is evaluated, which is like the nesting of control statements in most languages. Values flow back up from expressions to their callers, which is like the nesting of expressions in most languages.

You can write an expression that is an ordered sequence of other expressions, using begin. For example,

(begin (foo)
       (bar))

calls foo and then calls bar. In terms of control flow, a (begin ... ) expression is rather like a begin ... end block in Pascal, or a { ... } block in C. (We don't need an end keyword, because the closing parenthesis does the job.)

Scheme begin expressions aren't just code blocks, though, because they are expressions that return a value. A begin returns the value of the last expression in the sequence. For example, the begin expression above returns the value returned by the call to bar.

The bodies of procedures work like begins as well. If the body contains several expressions, they are evaluated in order, and the last value is returned as the value of the procedure call.

Here's a procedure baz that calls foo and then calls bar and returns the result from the call to bar.

(define (baz)
   (foo)
   (bar))

Go to the first, previous, next, last section, table of contents.