[Next] [Previous] [Up] [Top] [Contents] [Index]

14 The Built-In Macros and Special Operators

Statements

Statements are used to implement a variety of program constructs.

Many statements include an optional implicit body, which may contain one or more constintuents separated by semicolons. When an implicit body is executed, the expressions in the implicit body are executed in order (left to right). The values of the implicit body are the values of the last expression. If the optional implicit body is not present or contains no expressions, the return value is #f.
Statements (continued)
Macro DescriptionPage
if Executes an implicit body if the value of a test is true or an alternate if the test is false.381
unless Executes an implicit body unless the value of a test is true.383
case Executes a number of tests until one is true, and then executes an implicit body associated with the true test.383
select Compares a target object to a series of potential matches, and executes an implicit body associated with the first match found.384
while Repeatedly executes a body until a test expression is false.386
until Repeatedly executes a body until a test expression is true.386
for Performs general iteration over a body, updating bindings and performing end tests on each iteration.387
begin Executes expressions in a body, in order.390
block Executes a body with several options for non-standard flow of control.390
method Creates and returns a method.394

Conditionals

The following statements are used to perform conditional execution.

if [Statement]


Executes an implicit body if the value of a test is true or an alternate if the test is false.

Macro Call:
if ( test ) [consequent ]
{ elseif ( elseif-test ) [elseif-consequent ] }*
[ else [alternate ] ]
end [ if ]

Arguments:
test expressionbnf

consequent
bodybnf

elseif-test
expressionbnf

elseif-consequent
bodybnf

alternate
bodybnf

Values:
Zero or more instances of <object>.

Description:
if executes one or more expressions, executing and returning the values of a body following the first test which returns true.

test is the first expression to be executed. If its value is true, if executes and returns the values of the consequent. If the value of test is false, if proceeds with the optional elseif-tests and alternate.

First the elseif clauses are tried in order. The first elseif-test is executed. If its value is true, the corresponding elseif-consequent is executed and its values are returned as the value of the if statement. If its value is false, the next elseif-test is tried. This continues until a true elseif-test is found, or until there are no more elseif clauses.

If the test and all the elseif-tests are false, the alternate is executed and its values are returned as the value of the if statement. If there is no alternate, the if statement returns #f.

if ( x < 0 )
  - x;
end if;
if ( heads?(flip(coin)) )
  start(black);
else
  start(white);
end if
if (player1.money <= 0)
  end-game(player1)
elseif (player2.money <= 0)
  end-game(player2)
else
  move(player1);
  move(player2);
end if
if ( camel.humps = 1 )
  "dromedary"
elseif ( camel.humps = 2 )
  "bactrian"
else
  "not a camel"
end if;

unless [Statement]


Executes an implicit body unless the value of a test is true.

Macro Call:
unless ( test )
[ body ]
end [ unless ]

Arguments:
test expressionbnf

body
bodybnf

Values:
Zero or more instances of <object>.

Description:
unless executes test. If the value of test is false, then the body is executed and its values are returned by unless. If the value of test is true, the body is not executed and unless returns #f.

If there are no expressions in the body, then #f is returned.

unless(detect-gas? (nose))
    light(match)
end unless

case [Statement]


Executes a number of tests until one is true, and then executes an implicit body associated with the true test.

Macro Call:
case
{ test => consequent } *
[ otherwise [ => ] alternate ]
end [ case ]

Arguments:
test expressionbnf

consequent
[ constituentsbnf ] ;

alternate
[ constituentsbnf ] ;

Values:
Zero or more instances of <object>.

Description:
case executes the test in order, until it reaches a test which returns true. When it reaches a test which returns true, it executes the corresponding consequent and returns its values. Subsequent tests are not executed. If the corresponding consequent is empty, the first value of the successful test is returned.

As a special case, the name otherwise may appear as a test. This test always succeeds if there is no preceding successful test.

If no test is true, then case returns #f.

case
   player1.money <= 0
     => end-game(player1);
   player2.money <= 0
     => end-game(player2);
   otherwise
     => move(player1);
        move(player2);
end case;

select [Statement]


Compares a target object to a series of potential matches, and executes an implicit body associated with the first match found.

Macro Call:
select ( target [ by test ] )
{ matches => consequent }*
[ otherwise [ => ] alternate ]
end [ select ]

Arguments:
target expressionbnf

test
expressionbnf

matches
{ expressionbnf } ,+ | ( { expressionbnf } ,+ )

consequent
[ constituentsbnf ] ;

alternate
[ constituentsbnf ] ;

Values:
Zero or more instances of <object>.

Description:
select generates a target object and then compares it to a series of potential matches, in order. If it finds a match, it executes the corresponding consequent and returns the values of the consequent. If no match is found, an error is signaled.

The target is executed to produce the match object.

The test, if supplied, is a function used to compare the target object to the potential matches. The default test is ==.

One at a time, each match is executed and its value compared to target, in order. If a match is found, the corresponding consequent is executed and its values are returned. If the corresponding consequent is empty, #f is returned.

Once a match is found, subsequent matches and the corresponding bodies are not executed.

As a special case, the name otherwise may appear instead of a matches. This will be considered a match if no other match is found.

If there is no matching clause, an error is signaled. Because an otherwise clause matches when no other clause matches, a select form that includes an otherwise clause will never signal an error for failure to match.

Since testing stops when the first match is found, it is irrelevant whether the test function would also have returned true if called on later matches of the same clause or on matches of later clauses.

select ( career-choice(student) )
   art:, music:, drama:
     => "Don't quit your day job";
   literature:, history:, linguistics:
     => "That really is fascinating";
   science:, math:, engineering:
     => "Say, can you fix my VCR?";
   otherwise => "I wish you luck";
end select;
select ( my-object by instance? )
  <window>, <view>, <rectangle> => "a graphical object";
  <number>, <string>, <list> => "a computational object";
  otherwise => "I don't know";
end select

Iteration Constructs

while [Statement]


Repeatedly executes a body until a test expression is false.

Macro Call:
while ( test )
[ body ]
end
[ while ]
Þ #f

Arguments:
test expressionbnf

body
bodybnf

Values:
#f

Description:
while loops over body until test returns false.

Each pass through the loop begins by executing test. If test returns a true value, the expressions in the body are executed and the looping continues. If test returns false, the loop terminates and while returns #f.

until ( test ) [Statement]


Repeatedly executes a body until a test expression is true.

Macro Call:
until ( test )
[ body ]
end
[until]
Þ #f

Arguments:
test expressionbnf

body
bodybnf

Values:
#f

Description:
until loops over body until test returns true.

Each pass through the loop begins by executing test. If test returns false, the expressions in the body are executed and the looping continues. If test returns true, the loop terminates and until returns #f.

for [Statement]


Performs general iteration over a body, updating bindings and performing end tests on each iteration.

Macro Call:
for ( { for-clause } ,* |
{ { for-clause ,}* end-clause })
[ loop-body ]
[ finally [ result-body ] ]
end [ for ]

Arguments:
for-clause explicit-step-clause |

collection-clause |

numeric-clause

end-test
expressionbnf

loop-body
bodybnf

result-body
bodybnf

explicit-step-clause
variablebnf = init-value then next-value

collection-clause
variablebnf in collection

numeric-clause
variablebnf from start
[ { to | above |below } bound ]
[ by increment ]

end-clause
{ until: | while: } end-test

init-value
expressionbnf

next-value
expressionbnf

collection
expressionbnf

start
expressionbnf

bound
expressionbnf

increment
expressionbnf

Values:
Zero or more instances of <object>.

Description:
for iterates over loop-body, creating and updating iteration bindings on each iteration according to the for-clauses. Iteration ends when one of the for-clauses is exhausted, or when the optional end-test is satisfied.

Each for-clause controls one iteration binding. The optional end-test does not control any iteration bindings.

There are three kinds of for-clauses: explicit-step-clauses, collection-clauses, and numeric-clauses: An explicit-step-clause creates bindings for the results of executing an expression. A collection-clause creates bindings for successive elements of a collection. A numeric-clause creates bindings for a series of numbers.

Execution of a for statement proceeds through the following steps:

Execute the expressions that are executed just once, in left to right order as they appear in the for statement. These expressions include the types of all the bindings, and the expressions init-value, collection, start, bound, and increment. If the value of collection is not a collection, an error is signaled. The default value for increment is 1.

Create the iteration bindings of explicit step and numeric clauses.

for ( thing = first-thing then next(thing),
      until: done?(thing) )
  do-some(thing)
end;
for (j :: <integer> from 0 to height)
  for (i :: <integer> from 0 to width)
   erase(i,j);
   plot (i,j);
  end for;
end for;
for (city in olympic-cities,
     year from start-year by 4)
  schedule-olympic-game(city, year)
  finally: notify(press);
           sell(tickets);
end;
for (i from 0 below 100,
     zombies from 0 below 100,
     normals from 100 above 0 by -1)
   population[i] := zombies + normals
end;

begin [Statement]


Executes expressions in a body, in order.

Macro Call:
begin [ body ] end

Arguments:
body bodybnf

Values:
Zero or more instances of <object>.

Description:
Begin executes the expressions in a body, in order. The values of the last expression are returned. If there are no expressions in the body, #f is returned.

block [Statement]


Executes a body with several options for non-standard flow of control.

Macro Call:
block ( [ exit-variable ] )
[ block-body ]
[ afterwards [ afterwards-clause ] ]
[ cleanup [ cleanup-clause ] ]
{ exception exception-clause }*
end [ block ]

Arguments:
exit-variable variable-namebnf

block-body
bodybnf

afterwards-clause
bodybnf

cleanup-clause
bodybnf

exception-clause

( [ name :: ] type { ,exception-options }*)
[ bodybnf ]

name
variable-namebnf

type
expressionbnf

exception-options
{ test: expressionbnf } | { init-arguments: expressionbnf }

Values:
Zero or more instances of <object>.

Description:
block executes the expressions in the block-body in order, and then the executes the optional afterwards-clause and cleanup-clause. Unless there is a non-local exit, block returns the values of the block-body, or #f if there is no block-body.

If exit-variable is provided, it is bound to an exit procedure (an object of type <function>) which is valid during the execution of the block body and the clauses. At any point in time before the last clause returns, the exit procedure can be called. Calling the exit procedure has the effect of immediately terminating the execution of the block, and returning as values the arguments to the exit procedure.

The body of the afterwards-clause, if provided, is executed after the block-body. The values produced by the afterwards-clause are ignored.

The body of the cleanup-clause, if provided, is executed after the block-body and afterwards-clause. Its values are also ignored. The cleanup clause differs from the afterwards clause in that its body is guaranteed to be executed, even if the execution of the block is interrupted by a non-local exit. There is no such guarantee for the afterwards clauses.

For example, the following code fragment ensures that files are closed even in the case of an error causing a non-local exit from the block body:

block (return)
  open-files();
  if (something-wrong)
    return("didn't work");
  end if;
  compute-with-files()
cleanup
  close-files();
end block
The exception-clauses, if supplied, install exception handlers during the execution of the block-body, afterwards-clause, and cleanup-clause. If one of these handlers is invoked, it never declines but immediately takes a non-local exit to the beginning of the block, executes the expressions in its body and returns the values of the last expression or #f if the body is empty. Note that when the expressions in an exception body are executed, all handlers established by the block are no longer active. Note also that the cleanup clause of the block will be executed before the expressions of the handler body are executed.

The type and exception-options are as for let handler. If present, name is bound to the condition during the execution of the handler's body.

The exception clauses are checked in the order in which they appear. That is, the first handler will take precedence over the second, the second over the third, etc.

The following is a trivial use of an exception clause.

block (return)
  open-files();
  compute-with-files()
exception (<error>) 
  "didn't work")
cleanup
  close-files();
end block

Dynamic Extent of Block Features

A block installs features which are active for different portions of the execution of the block.

Intervening Cleanup Clauses

When an exit procedure is called, it initiates a non-local exit out of its establishing block. Before the non-local exit can complete, however, the cleanup clauses of intervening blocks (blocks that have been entered, but not exited, since the establishing block was entered) must be executed, beginning with the most recently entered intervening block. Once the cleanup clauses of an intervening block have been executed, it is an error to invoke the exit procedure established by that block. The cleanup clauses of the establishing block are executed last. At that point, further invocation of the exit procedure becomes invalid, and the establishing block returns with the values that were passed to the exit procedure.

Note that a block statement may also be exited due to the execution of a handler clause. Before the exception clause is executed, intervening cleanup clauses are executed as described above (including any clause for the establishing block.) The exit procedure may be invoked during execution of exception clauses, in which case the argument values are immediately returned from the block (the cleanup clause already having been executed).

During the process of executing the cleanup clauses of the intervening blocks, any valid exit procedure may be invoked and may interrupt the current non-local exit.

All exception clauses are executed in the same dynamic environment. None of the handlers established in the block are visible during the execution of one of the handlers. This can be thought of as parallel installation of the handlers.

Restrictions on the use of exit procedures

The exit procedure is a first-class object. Specifically, it can be passed as an argument to functions, stored in data structures, etc. Its use is not restricted to the lexical body of the block in which it was established. However, invocation of the exit procedure is valid only during the execution of the establishing block. It is an error to invoke an exit procedure after its establishing block has returned, or after execution of the establishing block has been terminated by a non-local exit.

In the following example, the block establishes an exit procedure in the binding bar. The block returns a method containing a call to bar, and the method is stored in the binding foo. Calling foo is an error because it is no longer valid to invoke bar after its establishing block has returned.

define constant foo =
  block (bar)
     method (n) bar(n) end;
  end block;
foo(5)
  {error or other undefined consequences}

method [Statement]


Creates and returns a method.

Macro Call:
method parameter-list [ body ] end [ method ]

Arguments:
parameter-list parameter-listbnf

body
bodybnf

Values:
An instance of <method>.

Description:
method creates and returns a method specified by the parameter-list and body. For a complete description of methods, see "Methods" on page 78.


Dylan Reference Manual - 17 OCT 1995
[Next] [Previous] [Up] [Top] [Contents] [Index]

Generated with Harlequin WebMaker