Manual
Parameters
 previous   up   next 

6. PARAMETERS

The following sub-chapters introduce the parameter types of Seed7.

Parameter Evaluation strategy Assignment to formal parameter
val aType: name call-by-value forbidden
ref aType: name call-by-reference forbidden
in aType: name depends on the type forbidden
in var aType: name call-by-value allowed (no effect on actual parameter)
inout aType: name call-by-reference allowed (changes actual parameter)
in func aType: name call-by-name forbidden

6.1 'val' parameter

Value parameters are introduced with the keyword 'val' (e.g.: val string: stri). A value parameter copies the value of the actual parameter. The formal 'val' parameter cannot be changed inside the function. Value parameters are used, when copying is cheap or when copying is necessary for the correct behavior. The function below appends a comma and a string to the variable 'globalStri':

const proc: appendStri (val string: stri) is func
  begin
    globalStri &:= ",";
    globalStri &:= stri;
  end func;

After doing

globalStri &:= "a";
appendStri(globalStri);

the variable globalStri contains the value "a,a". If the function header would be

const proc: appendStri (in string: stri) is func

the variable globalStri would contain the value "a,a,". This difference is because of the following reasons:

For arrays 'in' parameters are equal to 'ref' parameters. When appendStri is called with globalStri as parameter an unwanted side effect takes place: Every change of globalStri changes also the 'ref' parameter stri. Changes to the 'ref' parameter would also change the global variable. Such unwanted side effects can also take place between parameters (when at least one parameter is an 'inout' parameter).

In most cases such unwanted side effects are impossible or can be avoided easily. An 'in' parameter should be preferred over an 'val' parameter, when possible.

Semantics:
When calling a function a formal 'val' parameter gets its value from the corresponding actual parameter. This is done with a create procedure ( ::= ). Inside the function it is only possible to read a formal 'val' parameter. Changing a formal 'val' parameter is not possible. When a function is left a 'destr' procedure is called for every 'val' parameter. Formal 'val' parameters have the access right 'const'.
Syntax:
val_parameter ::=
'val' type_expression ':' identifier_declaration |
'val' type_expression 'param' .

Declaration:

$ syntax expr: .val.().param       is -> 40;
$ syntax expr: .val.(). : .(expr)  is -> 40;

const func f_param: val (ref type param) param               is action "DCL_VAL1";
const func f_param: val (ref type param) : (ref expr param)  is action "DCL_VAL2";

6.2 'ref' parameter

Reference parameters are introduced with the keyword 'ref' (e.g.: ref array string: arr). A reference parameter refers to the value of the actual parameter. The formal 'ref' parameter cannot be changed inside the function. Reference parameters are used, when copying is expensive and referring to the value does not change the correct behavior. The function below defines the primitive action for the semicolon operator:

const proc: (ref void: statement1) ; (ref void: statement2) is noop;

In this definition and other definitions of primitive actions 'ref' parameters are used. For normal functions usually 'in' parameters are used instead of 'ref' parameters:

const func integer: total_length (in array string: arr) is func
  result
    var integer: lengthSum is 0;
  local
    var integer: index is 0;
  begin
    for index range 1 to length(arr) do
      lengthSum +:= length(arr[index]);
    end for;
  end func;

Above function could also be defined with the following function head:

const func integer: total_length (ref array string: arr) is func

Since for array types (and also for struct types) 'in' parameters are defined to act as 'ref' parameters both definitions are equal. An 'in' parameter should be preferred over an 'ref' parameter, when possible.

Semantics:
When calling a function a formal 'ref' parameter is set to refer to the corresponding actual parameter. Inside the function it is only possible to read a formal 'ref' parameter. Changing a formal 'ref' parameter is not possible. Formal 'ref' parameters have the access right 'const'.
Syntax:
ref_parameter ::=
'ref' type_expression ':' identifier_declaration |
'ref' type_expression 'param' .

Declaration:

$ syntax expr: .ref.().param       is -> 40;
$ syntax expr: .ref.(). : .(expr)  is -> 40;

const func f_param: ref (ref type param) param               is action "DCL_REF1";
const func f_param: ref (ref type param) : (ref expr param)  is action "DCL_REF2";

6.3 'in' parameter

Input parameters are introduced with the keyword 'in' (e.g.: in integer: number). Depending on the type an input parameter is either a value or a reference parameter. The formal 'in' parameter cannot be changed inside the function. The function below checks if a given number is a prime number:

const func boolean: is_prime (in integer: number) is func
  result
    var boolean: prime is FALSE;
  local
    var integer: count is 2;
  begin
    if number = 2 then
      prime := TRUE;
    elsif number >= 3 then
      while number rem count <> 0 and count * count <= number do
        incr(count);
      end while;
      prime := number rem count <> 0;
    end if;
  end func;

The following function defines the ex (outer) product:

const func array array integer:
    (in array integer: a) ex (in array integer: b) is func
  result
    var array array integer: product is 0 times 0 times 0;
  local
    var integer: index1 is 1;
  begin
    product := length(a) times length(b) times 0;
    for index1 range 1 to length(a) do
      for index2 range 1 to length(b) do
        product[index1][index2] := a[index1] * b[index2];
      end for;
    end for;
  end func;

Although both examples use 'in' parameters the parameter in the first example is actually a 'val' parameter while the parameters in the second example are actually 'ref' parameters. When a new type is created with the 'newtype' function it is necessary to specify the meaning of the 'in' parameter. This is done with a call of the IN_PARAM_IS_VALUE or the IN_PARAM_IS_REFERENCE function with the new generated type as parameter. If a new type is created with the 'subtype' function this specification is optional since the base type has already a specification of the 'in' parameter.

Semantics:
Depending on the type an 'in' parameter is equivalent to an 'val' (call by value) parameter or to an 'ref' (call by reference) parameter. Formal 'in' parameters have the access right 'const'.
Syntax:
in_parameter ::=
'in' type_expression ':' identifier_declaration .

Declaration:

$ syntax expr: .in.().param       is -> 40;
$ syntax expr: .in.(). : .(expr)  is -> 40;

const func f_param: in (ref type param) param               is action "DCL_IN1";
const func f_param: in (ref type param) : (ref expr param)  is action "DCL_IN2";

const proc: IN_PARAM_IS_VALUE (ref type: aType)             is action "TYP_SET_IN_PARAM_VALUE";
const proc: IN_PARAM_IS_REFERENCE (ref type: aType)         is action "TYP_SET_IN_PARAM_REF";

6.4 'in var' parameter

Value parameters that can be changed inside the function are introduced with the keywords 'in var' (e.g.: in var integer: a). The value of the actual parameter is copied. Changes to the formal 'in var' parameter have no effect outside the function. The function below computes the greatest common divisor:

const func integer: gcd (in var integer: a, in var integer: b) is func
  result
    var integer: gcd is 0;
  local
    var integer: help is 0;
  begin
    while a <> 0 do
      help := b rem a;
      b := a;
      a := help;
    end while;
    gcd := b;
  end func;

Semantics:
When calling a function a formal in var parameter gets its value from the corresponding actual parameter. This is done with a create procedure ( ::= ). Inside the function it is possible to read and change a formal in var parameter. Changing a formal in var parameter has no effect on the actual parameter. When a function is left a 'destr' procedure is called for every in var parameter. Formal in var parameters have the access right var.
Syntax:
in_var_parameter ::=
'in var' type-expression ':' identifier_declaration .

Declaration:

$ syntax expr: .in.var.().param       is -> 40;
$ syntax expr: .in.var.(). : .(expr)  is -> 40;

const func f_param: in var (ref type param) param               is action "DCL_IN1VAR";
const func f_param: in var (ref type param) : (ref expr param)  is action "DCL_IN2VAR";

6.5 'inout' parameter

Reference parameters, that can be changed inside the function, are introduced with the keyword 'inout' (e.g.: inout integer: number). A reference parameter refers to the value of the actual parameter. Changes to the formal 'inout' parameter effect also the actual parameter. The procedure below doubles the given parameter 'number':

const proc: double (inout integer: number) is func
  begin
    number := 2 * number;
  end func;

Semantics:
When calling a function a formal 'inout' parameter is set to refer to the corresponding actual parameter. Inside the function it is possible to read and change a formal 'inout' parameter. Changing a formal 'inout' parameter changes the actual parameter as well. Formal 'inout' parameters have the access right 'var'.
Syntax:
inout_parameter ::=
'inout' type_expression ':' identifier_declaration .

Declaration:

$ syntax expr: .inout.().param       is -> 40;
$ syntax expr: .inout.(). : .(expr)  is -> 40;

const func f_param: inout (ref type param) param               is action "DCL_INOUT1";
const func f_param: inout (ref type param) : (ref expr param)  is action "DCL_INOUT2";

6.6 Call-by-name parameter

Call-by-name is an evaluation strategy for parameters. Call-by-name parameters are not introduced with a keyword. All parameters with a function type like func boolean or proc are a call-by-name parameters. The actual call-by-name parameter is not evaluated before the function is called. When the function is executed the call-by-name parameter might be executed once, many times or not at all.

For normal (not call-by-name) parameters the following holds: When the function is called the actual parameter expressions are evaluated and the results of the evaluation are forwarded to the function. Assume number has the value 12 and the following statement is executed:

seek(aFile, number + 5);

The expression number + 5 is evaluated to 17 before seek is called. The statement actually executed is:

seek(aFile, 17);

The definition of seek requests that its 2nd parameter must be an integer and not an integer expression:

const proc: seek (inout file: aFile, in integer: position) is ...

This triggers the evaluation of number + 5. Compare this to:

while number > 0 do
  number -:= 5;
end while;

The while-statement is not called with the result of number > 0. This would not work for the loop condition. The while-statement receives the expression number > 0 instead. This way the while-statement can decide how often number > 0 is evaluated. This contradicts the claim from above that actual parameter expressions are evaluated before the function is called. Obviously the condition parameter behaves different. This behavior is triggered by the declaration of the while-statement:

const proc: while (in func boolean: condition) do (in proc: statement) end while is ...

The parameter condition has the type func boolean which is exactly the type of the expression number > 0. For this reason number > 0 is forwarded to the while-statement without evaluating it. All parameters with a func type have this behavior. The common name for all of them is call-by-name parameter. All call-by-name parameters are not evaluated before the function is called.

Examples of normal and call-by-name parameters:

Normal parameter Call-by-name parameter
in boolean: name in func boolean: name
in integer: name in func integer: name
in string: name in func string: name

The type proc can also be used to define a call-by-name parameter:

const proc: possiblyDo (in proc: statement) is func
  begin
    if flipCoin then
      statement;
    end if;
  end func;

The function possiblyDo can be called with any statement (procedure):

possiblyDo(writeln("hello world"));

6.7 Symbol parameter

Some functions need symbols at fixed places in the parameter list. The following IF-statement requests the keywords 'THEN', 'END' and 'IF' at specific places:

IF condition THEN
  statement
END IF;

After defining the syntax of this IF-statement with

$ syntax expr: .IF.().THEN.().END.IF  is -> 25;

the semantic can be defined with:

const proc: IF (in boolean: condition) THEN
              (in proc: statement)
            END IF is func

              begin
                case condition of
                  when {TRUE}: statement;
                end case;
              end func;

The symbol parameters are just written outside the parentheses. A call of this statement could be:

IF value < maximum THEN
  write(value)
END IF;

Semantics:
Symbol parameters must be defined in a syntax definition and in a corresponding semantic definition. In the semantic definition symbol parameters are written outside of the parentheses. In the actual parameter list the corresponding symbol of the formal parameter list must be written.
Syntax:
symbol_parameter ::=
name_identifier | special_identifier .

6.8 'attr' parameter

This declaration associates a name to the type 'char':

const string: name (attr char) is "char";

This 'name' can be used as follows:

writeln(name(char));

It is possible to overload such declarations:

const string: name (attr boolean)  is "boolean";
const string: name (attr float)    is "float";

An 'attr' parameter can be used in a function also:

const func char: (attr char) parse (in string: stri) is
  return stri[1];

Semantics:
The actual parameter which corresponds to an 'attr' parameter must be the type mentioned in the declaration of the 'attr' parameter. An 'attr' parameter does not declare a formal parameter variable which could be used inside a function.
Syntax:
attr_parameter ::=
'attr' type_expression .


 previous   up   next