LARD Language Language Reference Manual

Declarations and Scope

Declarations

Declarations are introduced by means of the keyword . (or more accurately by means of the expression _._). The expression D . E has the value of the expression E in the context of the declaration D.

Declarations can take three forms:

The last of these is used to include definitions from a library, and is described in a later section.

The prototype indicates the name of the thing being declared. The class indicates what sort of thing it is, and the definition, if present, defines it. All three of these are just expressions.

Possible classes of declarations include types, variables and expressions, and are described later.

The prototype may be either a single identifier, as would be the case for a variable declaration, or it may be an expression with parameters. These parameters, if present, must themselves be declarations. The following examples illustrate this:
v:var(int)v is a variable of type int.
rand:expr(int)=lfsr(seed)rand is an expression of type int, with the given defintion.
max(a:val(int),b:val(int)):expr(int)=(if (a>b) then a else b) max is an expression of type int, which takes two arguments, a and b, each of which is a value of type int.

Scope

The scope of a declaration is the rest of the expression in which it appears. So a declaration introduced using . is visible only within the body expression of the . expression. A parameter declaration is visible within the whole of the declaration whose prototype it appears in.

Libraries may be used to extend the scope of identifiers.

Scope and Concurrency

Where a declaration preceeds the point at which flow of control forks into concurrent threads the declaration is shared by all threads. Where a declaration is within an expression that executes concurrently with some other expressions, the declaration is private to that thread.

Classes

Each expression has a class. The lard compiler finds the class of each expression as it parses the program and checks that the classes of parameter expressions match the classes of the coresponding parameter declarations. This class checking is a superset of type checking.

The classes are as follows:
classThe class of classes. This is used for bootstrapping only; users should never declare things to have class class
typeThe class of types.
val(T)For each type T there is a class of values of that type.
var(T)For each type T there is a class of variables of that type.
expr(T)For each type T there is a class of expressions of that type.
libThe class of libraries. See the section on libraries.

Normal Declarations

By Normal Declaration I mean declarations that aren't parameter declarations.

Type Declarations

Type declarations may take either of the following forms:

Value Declarations

Value declarations have two forms:

Variable Declarations

A variable declaration introduces a new local variable. The variable is creatred when the enclosing expression begins evaluation and is destroyed when it completes. Variables are initially undefined. There is currently no mechanism for giving initial values to variables, and variable declarations must have no definition.

Some example variable declarations are:

Expression Declarations

Expression declarations define functions or procedures. If no definition is present the expression is built in; this should occur only in library code.

Some example expression declarations are:

Parameter Declarations

The meanings of declarations a slightly different when they are the declarations of parameters to type or expression declarations. Parameter declarations never have definitions, irespective of their class.

Type Declarations

A type parameter declaration declares a parameter that matches a type expression given in the call. The type parameter may subsequently be used in other parameter declarations, variable declarations etc. For example: Type parameter declarations are useful as implicit parameters; see a later section.

Value Declarations

A value parameter declaration declares a parameter that is passed by value. A copy of the parameter is taken, and is read-only within the body of the expresssion. See the max example elsewhere.

Variable Declarations

A variable parameter declaration declares a parameter that is passed by value. Within the body of the expression the parameter can be assigned to in the same way as a local variable. See the swap example elsewhere.

Expression Declarations

An expression parameter declaration matches an unevaluated expression. This is used in contexts where the expression may evaluate its parameters more than once, such as the while_do_ function, or where it may not evaluate them at all, such as the if_then_else_ function. The following example shows how repeat_until_ can be implemented in terms of while_do_:

repeat_until_(cond:expr(bool),body:expr(void)):expr(void)=(
  body;
  while (!cond) do
    body
)

Implicit Declarations

Implicit declarations are declarations that appear within the class expressions of parameter declarations. They match sub-expressions of the class of the paramter. They are used to allow expressions to accept generic parameters without having explicit type parameters, as in the swap example presented previously.

Here is how the swap example can be re-implemented using an implicit declaration:

swap(a:var(T:type),b:var(T)):expr(void)=
((tmp:var(T)).((tmp:=a);(a:=b);(b:=tmp)))
This can be called as swap(a,b) where a and b are variables of any type - as long as they are the same type as each other.

This example shows how implicit parameters can match subexpressions of the type expression:

length(v:((T:type)^(n:int))):expr(int)=n

Literal Declarations

Within the standard library it is legal to use a literal as the prototype of a declaration. This is used to associate the types int, char and string with numeric, character and string literals.

Libraries

A library declaration takes the following form:

name:lib=(
  (foo:expr(void)=printstr("foo\n")).
  (sprocket:expr(int)=99).
  islib
)
The declarations within the library declaration are not visible in the scope of the library declaration. To make them visible within some scope, use a use declaration:

use(name)