Elan Language Reference

# Named values

In most programming, the term variable describes a data entity in a program that contains a value and has a name by which you can read and write its value, i.e. its value can be varied. In an Elan program, these data entities are called named values so that a distinction can be made between:

- mutable named values whose content can be assigned and re-assigned, e.g. defined with a variable instruction and re-assigned with a set instruction or, for mutable data structures, there are methods for their update.
- immutable named values whose content is assigned, e.g. with a let instruction, and then cannot be re-assigned.

This distinction applies both to single values and to data structures (which contain multiple values) and, while it is most pertinent in functional programming, it provides a useful discipline in any code. Preferring immutable named values can help avoid duplication and assist in the modularisation of code.

Both kinds of named value may be initialised with either literals or expressions.

## Identifiers

For every named value, its name must follow the rules for an identifier:

- It must start with a lowercase letter followed by any combination ot letters (upper- or lowercase), digits and underscore '_' symbols.
- It must not contain spaces or other symbols.
- It cannot be the same as any language keyword, method or other reserved word. If you happen to choose such a name, an error message will tell you that it cannot be used for an identifier.

## Named value Types

Named values are statically typed: when initialised, their Type is established and cannot be changed.

The Types used in the language are here listed, each linking to its definition and further details:

Type group | Types
--- | ---
Fundamental | Integer Int | Floating point Float | Boolean Boolean | Character string String |
Mutable data structures | List List | Look-up dictionary Dictionary | Simple array Array | 2-dimensional array Array2D |
Immutable data structures | Immutable list ListImmutable | Immutable look-up dictionary DictionaryImmutable | Set Set | LIFO stack Stack | FIFO queue Queue
Data I/O | Text file input TextFileReader | Text file output TextFileWriter |  |  |
Graphics | Turtle | SVG vector graphics |  |  |
User-defined | Enumeration enum Name | Record record Name | Class class Name | Abstract class abstract class Name | Interface interface Name
Other | Random number Random | Function reference Func |  |  |

Assignments that define named values with fundamental Types don't require the Type name to be specified. Type names are, however, required in:

- >parameters of functions and procedures

- return value of a function
- property of an interface, class or record
- empty named value, e.g. empty List
- Data structures whose elements have a particular Type, e.g. Array<of Int>

## Re-assigning vs. mutating named values

This table shows which named values can be re-assigned and/or mutated depending on how they were defined:

define named value | named value | notes
--- | --- | ---
with | as mutable Type | as immutable Type | re-assign with 'set' | mutate | scope
constant | ✘ | ✔ | ✘ | ✘ | global | constant values are set only at compile time
let | ✔ |  | ✘ | ✔ | local |
let |  | ✔ | ✘ | ✘ | local |
variable | ✔ | ✔ | ✔ | ✔ | local |
parameter | ✔ |  | ✘ | ✔ | local | formal input argument of function, procedure or lambda
parameter |  | ✔ | ✘ | ✘ | local | formal input argument of function, procedure or lambda
out parameter | ✔ |  | ✔ | ✔ | local | formal output argument of procedure
out parameter |  | ✔ | ✔ (see note) | ✔ | local | formal output argument of procedure an out parameter of an immutable Type may be re-assigned if the actual argument is defined with variable, not let

# Global instructions

Global instructions (also referred to as globals) are located directly in your code at the highest level. They are never indented from the left-hand edge, nor may they be located within other instructions. They are: main, procedure, function, test, record, class, abstract class, interface, constant and enum.

- main, procedure and function are described as methods and these are defined by one or more statements within them.
- test also contains statements, in particular the assert statement for testing functions during development of your program.
- record, class, abstract (class), and interface define data structures and these always contain members.
- constant and enum do not contain any further instructions – only names and values.
- The sequence of global instructions in your code is immaterial, with two exceptions:
  - If one constant is used in the definition of another constant, the first must be declared above the second.
  - An abstract class must be declared above any class that inherits from it.

## main

A program must have a main method, sometimes called its main routine, if it is intended to be run as a program. You may, however, develop and test code that does not have a main method, either as a coding exercise or for subsequent use within another program.

The main method defines the start point of a program when it is run (executed).

It does not have to be at the top of the file, but this is a good convention to follow.

It may delegate work to one or more procedures or functions.

There may not be more than one main in a program file, and the Global prompt will not show main as an option when one already exists in the file.

#### Example of a main method

▶ A main method

+main variable li name set to [3, 6, 1, 0, 99, 4, 67]expression call inPlaceRippleSort procedureName(li arguments) print li expression end main

## procedure

A procedure is a named piece of code that can define parameters which are given inputs via arguments in a call statement. Its name must follow the rules for an identifier.

Unlike a function:

- a procedure does not return a value (unless its parameters include an out value).
- a procedure can have side effects: indeed it should have side effects, otherwise there would be no point in calling it!

Therefore the statements within a procedure can:

- include print statements and methods.
- include data input methods and other system methods (such as for random number generation).
- call other procedures (or itself if recursion is required).
- assign a value to a parameter, provided that the parameter definition is preceded by the keyword out.

#### Examples of procedures

▶ Calling a procedure:

variable li name set to [3, 6, 1, 0, 99, 4, 67]expression call inPlaceRippleSort procedureName(li arguments) print li expression

▶ Using keyword out

+procedure inPlaceRippleSort name(out arr as List<of Int>parameter definitions) variable changes name set to true expression variable lastComp name set to arr.length() - 2 expression +repeat set changes variableName to false expression +for i variableName from 0 expression to lastComp expression step 1 expression +if arr[i] > arr[i + 1]condition then let temp name be arr[i]expression call arr.put procedureName(i, arr[i + 1]arguments) call arr.put procedureName(i + 1, temp arguments) set changes variableName to true expression end if end for set lastComp variableName to lastComp - 1 expression end repeat when not changes condition end procedure

## function

A function is a named piece of code that can define parameters which are given inputs via arguments when reference to the function occurs in a statement or expression. Its name must follow the rules for an identifier.

Unlike a procedure:

- a function always returns a value.
- a function can have no side effects (i.e. no references outside its code) and cannot depend on any System methods.
- a function's execution cannot be interrupted by any external event, though a a non-terminaring loop in a function may be detected with tests that time out.

The return statement is followed by the value returned by the function.

#### Example of a function and reference to it

▶ The function definition

+function score(g as Game) returns Int return g.body.length() - 2 end function

▶ Reference to the function

+main let g be new Game() print score(g) end main

### Dot syntax

The function example above includes two uses of dot syntax to qualify items in an expression:

- g.body refers to property body (defined in class Game) to return its current value in the class instance named g.
- g.body.length() applies method length to the value g.body to return its string length.
  This use of dot syntax is usually referred to as applying a dot method.
  Examples include the common dot methods

Dot syntax is also used:

- in a call to procedure method referencing a variable, as in: variable a4 set to new Array<of Int>(4, 0) call a4.put(2, 5)

  The procedure called may be defined in a class or provided by the system.

- to deconstruct a Tuple.

You cannot, however, use dot syntax with your user-defined procedures at the global level,
nor with some system provided functions (e.g. abs) and procedures (e.g. clearPrintedText).

### Parameters

Parameters for both a procedure and a function are defined in exactly the same way. Each parameter definition takes the form:
<named value> as <Type>
for example: age as Int parameter definitions

### Recursion

Procedures and functions may be called or referenced recursively. For example, a simple factorial calculation: +function factorial(n as Int) returns Int return (if n > 1
then n*factorial(n - 1)
else 1) end function

## test

A test instruction is at the global level, and consists of a set of assertions about the outputs of functions.

Without having to run your program, the assert statements show whether your assertions pass or fail.

#### Example of a set of assert statements that test a function

▶ Test function binarySearch on three test lists

+test optional description let li1 name be ["lemon", "lime", "orange"]expression assert binarySearch(li1, "lemon")computed value is true expected value pass assert binarySearch(li1, "lime")computed value is true expected value pass assert binarySearch(li1, "orange")computed value is true expected value pass assert binarySearch(li1, "pear")computed value is false expected value pass let li2 name be ["lemon", "orange"]expression assert binarySearch(li2, "lemon")computed value is true expected value pass assert binarySearch(li2, "orange")computed value is true expected value pass assert binarySearch(li2, "pear")computed value is false expected value pass let li3 name be ["lemon"]expression assert binarySearch(li3, "lemon")computed value is true expected value pass assert binarySearch(li3, "lime")computed value is false expected value pass let li4 name be empty List<of String>expression assert binarySearch(li4, "pear")computed value is false expected value pass end test

#### Notes

- Elan tests are designed to test functions only. It is not possible to call a procedure or main routine within a test. Nor is it possible to use any System method (the same rule as for a function).
- A test may optionally be given a name or description in free-form text, just like a comment, which plays no role in the execution of the test. You might give the test the same name as a function that it is testing, or you might describe a particular scenario that is being tested.
- test instructions may be written anywhere in the code, provided they are at the global level.
- A test instruction may contain any number of assert statements. The test runner is part of the IDE and it automatically attempts to run all assert statements showing each one's pass or fail outcome alongside. However, if the test hits a runtime error (as distinct from an assert failure) then execution of the test will stop and remaining asserts will be shown as 'not run'.
- In addition to assert statements, a test may contain any other statements that may be added into a function.
- All assert statements should be at the top level within the test frame; none may be put into a loop structure.

#### Testing Float values

When testing Float values it is recommend that you use the round method to round the computed result to a fixed number of decimal places. This avoids rounding errors and is easier to read. For example:

+test round()optional description assert sqrt(2).round(3)computed value is 1.414 expected value pass end test

#### Testing for runtime errors

If the expression you are testing would cause a runtime error then the error will be displayed in the red fail message:

+test let a be [5, 1, 7] assert a[0] is 5 pass assert a[2] is 7 pass assert a[4] is 0 actual (computed): Out of range index: 4 size: 3 assert a[4] is "Out of range index: 4 size: 3" pass end test

If there are failures, mark the tests that you added since the last successful test as ghosted and then remove their ghosted status one by one until the cause is identified and fixed.

In the last assert above, note how testing can also be done against an expected error message.

#### Ghosting a test to have it ignored

To ignore tests, use ghosting either on a test:

+test clockTick let g1 be newGame(new Random()) let g2 be newApple(g1) let g3 be clockTick(g2, "s") assert g3.head is newSquare(22, 16) pass assert g3.body.length() is 2 pass end test

or just on specific asserts:

+test clockTick let g1 be newGame(new Random()) let g2 be newApple(g1) let g3 be clockTick(g2, "s") assert g3.head is newSquare(22, 16) pass assert g3.body.length() is 2 pass end test

Even when tests or asserts are ghosted, all the tests will be run and their status shown, but the overall test status will show the status of only the unghosted tests (green pass, amber warning or red fail).

#### Non-terminating loops and recursion

The principal reason for ghosting a test is when either the test code, or code in any function being called, does not terminate. This typically means that there is a loop (or a recursive call) with no exit condition, or where the exit condition is never met.

If you do create such code without realising it, then when the tests are executed the test runner will time out after a few seconds (most tests will pass in milliseconds), and an error message will appear. Your priority should then be to identify the cause of the timeout and attempt to fix it before then unghosting the test.

## record

A record is a user-defined data structure that is given a Type name (which must begin with an uppercase letter). The record defines one or more properties, each of which has a name (starting with a lowercase letter) and a Type. The Type of a property may be any simple value Type, or a ListImmutable, or another Type of record ( or even of the same Type of record).

Note that a record has some similarity to a class in that:

- Both are user-defined data structures
- Both are given a Type name
- Both may define one or more properties, each with a name and Type
- Both may be created or copied using a with clause
- Both may define encapsulated methods

However a record differs from a class in that:

- A record is immutable (like a ListImmutable or a String). You can create a copy with specified differences but you cannot modify a property on a given instance.
- A record does not define a constructor
- A record may define only function methods, since procedure methods would imply the ability to mutate the record.

Examples: +record Square Name property x name as Int Type property y name as Int Type end record +record Game Name property head name as Square Type property body name as ListImmutable<of Square>Type property priorTail name as Square Type property apple name as Square Type property isOn name as Boolean Type property rnd name as Random Type property graphics name as BlockGraphics Type property key name as String Type end record Having defined a record's Type, such as Game above, you can create as many instances as you wish using the following syntax to specify the values: let g1 name be new Game() with
head set to newSquare(22, 15),
key set to "d",
isOn set to true expression

Note that you are not required to provide a value for each property because, where a property is not specified in the with clause (as above), that property will be given the empty (default) value of the correct Type.

You can then read the values from the properties using dot syntax for example:

print sq.size expression

record Types are immutable: the properties on an instance may not be changed directly. However, you can easily create another instance that is a copy of the original, with all the same property values except for specific changes made in a with clause. The newly-minted copy (with changes) must be assigned to a new named value. For example:

let sq1 name be new Square() with
x set to 3.5,
y set to 4.0,
size set to 1.0 expression let sq2 name be copy sq1 with
size set to 2.0,
colour set to red expression

Or even to the same name if that name is a variable:

variable a set to
set a to

This last example shows how you enter the comma-separated with clauses. The earlier examples show how the Editor displays a set of with clauses.

If you want to use one or more existing property values in order to determine a new value, the property names must be prefixed with the name of the instance being copied, for example:

let sq2 name be copy sq1 with
size set to sq1.size + 3 expression

#### Record deconstruction

A record may be deconstructed, meaning that its properties are read into separate variables using the same syntax as for deconstructing a Tuple. For example, assuming that Square is a record defined as in the example above, then this code:

let x, y, size, colour name be mySquare expression

will read the properties into the four names defined.

When deconstructing, the names of the values must match the names of the properties of the record. However, the ordering of the names does not have to match the order in which the properties are defined in the record.

## class

A class is a user-defined Type offering richer capability than an enum or a record.

Unlike a record, a class may have a constructor and procedure methods.

Like any other Type its name must begin with an uppercase letter.

#### Definition

Here is an example of class definition, taken from demo program snake_OOP.elan:

+class Apple Name property location as Square +procedure newRandomPosition(snake as Snake) +repeat let ranX be randomInt(0, 39) let ranY be randomInt(0, 29) set property.location to new Square(ranX, ranY) end repeat when not snake.bodyCovers(property.location) end procedure +procedure updateBlocks(blocks as Array2D<of Int>) call blocks.put(property.location.x, property.location.y, red) end procedure end class

A class may define:

- One or more properties – see Property
- function methods – see Function method
- procedure methods – see Procedure method
- a constructor which may be used for setting up the values of properties. The constructor may optionally define parameters to force the calling code to provide initial values. However, it is not necessary to add a constructor if you have no need to initialise properties. Code in the constructor may make use of any functions, and follows the same constraints as a function (i.e. it may not call any procedure, whether defined on the class or outside).

### Using a class

A class is instantiated using the keyword new followed by the class name and brackets, which should enclose the comma-separated arguments required to match the parameters (if any) defined on the constructor for that class. For example (also from demo program snake_OOP.elan):

+constructor(parameter definitions) let tail be new Square(20, 15) set property.currentDir to Direction.right set property.body to [tail] set property.head to tail.getAdjacentSquare(property.currentDir) set property.priorTail to tail end constructor

The created instance may then be used within expressions, like any other named value.

### this

If in the code of class A you want to invoke a method in class S and pass to it the current instance of A, you can refer to this instance with the keyword this, as shown by this line in demo program snake_OOP.elan: call apple.newRandomPosition(this)

Here, method newRandomPosition is defined on class Apple which needs to be passed an instance of class Snake. (apple is an instance of class Apple).

### Subclass

### Superclass

### Inheritance

### inherits

An ordinary class (also known as a concrete class) may optionally inherit from just one abstract class but may additionally inherit from any number of interfaces. The concrete class must define for itself a concrete implementation of every abstract member defined in the abstract class or any interfaces that it inherits from, directly or indirectly.

#### Notes

- An abstract class must be declared in the code above any class that inherits from it.
- The abstract class (if any) and the interfaces (if any) that a concrete class inherits from may not contain duplicates of any abstract member. Any duplicated definitions in the hierarchy will result in a compile error. If such duplications arise, you should factor out the common member definitions, and move them up the hierarchy or into new interfaces inherited by the interfaces and/or classes that need them.
- Inheritance hierarchies must form a tree, that is you must avoid creating a circular dependency where, for example, Type A inherits from Type B, which inherits from Type C, which inherits from Type A.
- The various 'super-Types' (abstract classes and interfaces) that a concrete class inherits from must not define conflicting members, e.g. members with the same name but having different Type signatures.
- See also example in Interface

## abstract class

An abstract class may not be instantiated (and hence may not define a constructor). It may define concrete members i.e.:

- a property
- a function
- a procedure

As with a concrete class, any of these members may be made private, after the corresponding frame has been added, by selecting that member frame and keying Ctrl-p.

These concrete members are automatically inherited by any subclass, but they may not be overridden (re-defined) by the subclass. Therefore you should define concrete members only if they are intended to work identically on every subclass.

You may also define abstract methods on an abstract class, i.e. abstract property, abstract function, abstract procedure. Such methods define only the signature of the method, not the implementation (body), therefore they have no end statement. For example:

abstract function calculateDiscount() as Float

If you wish to have several subclasses of an abstract class that share a common implementation for a method, but require that some of the subclasses can define a different implementation, then you should:

- Define the method as abstract on the superclass.
- Define a concrete implementation on the superclass with a similar, but slightly different, name e.g. by adding a prefix such as: default.
- Each subclass must then define its implementation of the abstract method, but the ones needing a common implementation can be just one line, delegating responsibility up to the 'default' method on the superclass.

## interface

An interface is similar to an abstract class, with the difference that it may define only abstract members. The advantage of using an interface instead of an abstract class is that a concrete class can inherit from multiple interfaces.

An interface may inherit only from other interfaces.

Important: An interface must not re-declare abstract interfaces that are defined in any interface it inherits from, directly or indirectly.

See also: Inheritance.

#### Example using interface

▶ Example from demo program blackjack.elan of:

- inheritance from interface Player to abstract class Automated and to class HumanPlayer, and
- inheritance from abstract class Automated to concrete class Dealer
- (concrete) definitions of function getAction and procedure changeScoreBy following their abstract declarations.

+interface Player Name abstract property hand as Hand abstract procedure changeScoreBy(amount as Int) end interface +abstract class Automated Name inherits Player property hand as Hand property score as Int abstract function getAction(parameter definitions) returns Action +procedure changeScoreBy(amount as Int) set property.score to property.score + amount end procedure end class +class Dealer Name inherits Automated +constructor(startingPoints as Int) set property.score to startingPoints end constructor +function getAction(parameter definitions) returns Action return if property.hand.total < 17
then Action.draw
else Action.stand end function end class +class HumanPlayer Name inherits Player inherits ClassName(s) +constructor(startingPoints as Int) set property.score to startingPoints end constructor +procedure changeScoreBy(amount as Int) set property.score to property.score + amount end procedure end class

## constant

A constant defines a named value that cannot change, is always defined at global level in a program, and is global in scope. Its name follows the rules for an identifier.

A constant is defined by a literal of an immutable Type, namely Int, Float, Boolean, String, ListImmutable or DictionaryImmutable.

Constants are created at compile time, so cannot be defined with reference to any function, nor can you use any operators in an expression.

A constant can be defined by a previously defined constant or a system constant, but take care not to redefine a system constant such as pi or blue without good reason.

Examples of literal definitions of the valid Types of constant:

+constant maxHits set to 10 — an Int value +constant turquoise set to 0x00ced1 — a hexadecimal Int value +constant liveCell set to black — a copy of a system constant +constant phi set to 1.618 — a Float value +constant gameOver set to true — a Boolean value +constant warningMsg set to "Limit reached" — a String value +constant fruit set to {"apple", "orange", "banana"} — a ListImmutable +constant palette set to {red, blue, yellow, turquoise} — a ListImmutable of system and above defined constants +enum Suit spades, hearts, diamonds, clubs — an enum for use in the following constant colours +constant colours set to {Suit.spades:black, Suit.hearts:red, Suit.diamonds:red, Suit.clubs:black} — a DictionaryImmutable using enum Suit +constant scrabbleValues set to {"A":1, "B":3, "C":3, "D":2, "E":1, "F":4, "G":2, "H":4, "I":1, "J":8, "K":5, "L":1, "M":3, "N":1, "O":1, "P":3, "Q":10, "R":1, "S":1, "T":1, "U":1, "V":4, "W":4, "X":8, "Y":4, "Z":10} — a DictionaryImmutable

## enum

An enum – short for 'enumeration' – provides for the simplest form of user-defined Type. You define it with a Type name (so starting with an uppercase letter) followed by a number of values (which must be valid identifiers).

A reference to an enum by its Type name necessarily holds one of the values.

Reference to the value in an enum is by using dot syntax enumType.enumValue.

An enum is read-only: once it has been defined it is not possible to add, remove, or update its values.

Examples of definition and use:

+enum Action Name stand, draw values +enum Outcome Name undecided, win, lose, draw, winDouble values +enum Status Name pending, playing, standing, blackjack, bust values variable status name set to Status.pending expression

And from demo program snake.OOP.elan, some code lines demonstrating reference to the enum Direction and to its values:

+enum Direction up, down, left, right +constructor(parameter definitions) set property.currentDir to Direction.right end constructor +function getAdjacentSquare(d as Direction) returns Square

# Member instructions

Member instructions (also referred to simply as 'members') are located within an interface, abstract class, (concrete) class or record. The new code prompt will offer a context appropriate subset of the following member instructions:

abstract property, abstract function, abstract procedure, property, function method, procedure method and constructor,

together with these 'private' versions:

private property, private function and private procedure.

This table shows which kinds of property, function or procedure, and constructor are applicable to the various kinds of class.

| Abstract | Concrete | notes
--- | --- | --- | ---
| interface | abstract class | class | record |
abstract property | ✔ | ✔ |  |  |
abstract function | ✔ | ✔ |  |  | prototype for function reference only
abstract procedure | ✔ | ✔ |  |  | prototype for procedure call only
property |  | ✔ | ✔ | ✔ |
function method |  | ✔ | ✔ | ✔ |
procedure method |  | ✔ | ✔ |  |
private property |  |  | ✔ | ✔ |
private function |  |  | ✔ | ✔ |
private procedure |  |  | ✔ |  |
constructor |  |  | ✔ |  |

## abstract property

An abstract property may be defined only on an abstract class. Any concrete subclass must then implement a concrete (regular) property to match.

## abstract function

An abstract function method may be defined only on an abstract class. Any concrete subclass must then implement a concrete (regular) function to match.

## abstract procedure

An abstract procedure method may be defined only on an abstract class. Any concrete subclass must then implement a concrete (regular) procedure to match.

## property

A property is a named value defined on a class with a name conforming to the rules for an identifier, and a Type (which may be another class name), for example:

    property height as Int
    property board as Board
    property head as Square
    property body as [Square]

It may be given an initial value within a constructor but if it is not thus initialised, then it will be given the default value empty for its Type. You can test whether a property contains this default thus:

    property names List
    ...
    if names is empty List

A property may be read, but not written to. Properties may be modified only from outside the class by means of a Procedure method.

## function method

A function method follows the same syntax and rules as a global function. The differences are:

- A function method is always referenced (used) by code outside the class using dot syntax on an instance.
- A function method may directly reference (read only) any property defined on the class as though it were a variable or parameter.
- A function method may be marked private, in which case it is visible only to code within the class and, if defined on an abstract class, within its subclasses. This is done by selecting the property frame and then keying Ctrl-p. (Pressing these keys again will remove the private modifier).
- asString() method
- asString method. This is just a regular function method with a specific name, no parameters and returning a String. If defined for a class, then if an instance of the class is printed, the asString function method will automatically be used. Typically asString will return a string made up of one or more of the property values, perhaps with additional text, or the results of function calls.

## procedure method

A 'procedure method' follows the same syntax and rules as a global procedure. The differences are:

- A procedure method, like a function method, is always referenced (used) by code outside the class using dot syntax on an instance.
- A procedure method may read, or write to, any property defined on the class.
- A procedure method may be marked private, in which case it is visible only to code within the class and, if defined on an abstract class, within its subclasses. This is done by selecting the property frame and then keying Ctrl-p. (Pressing these keys again will remove the private modifier).

## private (property, function, procedure)

A property may be marked private, in which case it is visible only to code within the class and, if defined on an abstract class, within its subclasses. This is done by using the context menu on the property frame or selecting it and keying Ctrl-p. This action is a toggle used both to set and to remove the modifier private.

- A property that is not private may be read, but not written to.
- A property that is private may

- Whenever you wish to access a property from within a method (or from within the constructor) on the same class, then the name of the property must be prefixed with the 'qualifier': property. ('property-dot'). This applies whether you are reading or setting the property. By this means you can have a method parameter with the same name as a property, but they are unambiguous, because the property must be prefixed. A common pattern is to use the same name in a 'setter' method, for example:

constructor(board as Board)
set property.board to board
end constructor
procedure setHeight(height as Int)
set property.height to height
end procedure

## constructor

A (concrete) class may have an optional constructor so as to:

- initialise properties with fixed values
- define parameters which are used to initialise properties

If a class does define a constructor, and the constructor defines parameters, then when the class is instantiated (using new) the values of the correct Type must be provided. For example, if the class Square has this constructor:

+constructor(x as Int, y as Int parameter definitions) set property.x variableName to x expression set property.y variableName to y expression end constructor

then it may be instantiated like this:

let tail name be new Square(20, 15)expression

# Statement instructions

Statement instructions (also referred to simply as 'statements') are the imperative keywords used in the methods (procedural logic) of a program, and are:

assert, call, each, for, if..then..else if..else, let, print, repeat, set, throw, try, variable, while.

## assert (test)

The assert statement is used only within a test, which is the mechanism for running unit tests during program development, not during program execution.

Some programming languages have a feature for making assertions while your program is running. In Elan, you can get equivalent functionality by throwing an exception, as described in throw statement.

## call (procedure)

A call statement is used when you want to run a procedure.

The procedure may be:

- a procedure that you have defined at the global level
call fillRandom(grid)
- a procedure method on an object of a class that you have defined
call apple.newRandomPosition(snake) call property.hand.draw()
- a procedure method that belongs to the same class as the procedure that you are calling from
call updateNeighbours()
- a procedure provided by the standard library
call pause(2000)
- a procedure method on an object of a Type provided by the standard library
call vg.append(rect) call property.cards.append(card)

The arguments provided must match the number and Type of the parameters specified in the definition of the procedure. If there are no parameters, leave the brackets empty.

For procedures that you define yourself, out parameters are allowed. In this case the corresponding argument must be the name of a variable whose value gets updated by the procedure.

If the parameter is not an out parameter, any expression of the correct Type can be used as an argument.

Procedures may have side effects, for example input/output or changing a data value in an object. They can change the contents of any mutable object passed in as an argument. For this reason, procedures cannot be called from functions, which are not allowed to have side-effects. call statements are simply not allowed in functions, to enforce this.

There is a limit to the complexity of a call statement. Only one dot is allowed in the procedure name field, or two dots if the first word is property. If you need anything more complicated, use a let statement on the line above. See the error message explanation for 'procedureName' in a call statement.

## each (loop)

The each...in.. construct specifies looping sequentially over the items in a List or an Array, or over the characters in a String.

The each loop counter variable is of the same Type as the items in the List or Array, or is of Type String if looping over the characters in a String. The variable does not have to have been previously defined.

#### Examples using each

▶ To print a List and then each item in it

variable names name set to ["Tom", "Dick", "Harriet"]expression print names[0]expression +each n variableName in names source print n expression end each

▶ Function to reverse a String

+function reverse(s as String) returns String variable sReturn set to empty String +each ch in s set sReturn to ch + sReturn end each return sReturn end function

#### Technical note about each

▶ The each instruction creates its own working copy of the subject values through which to loop sequentially. So even if the subject value is changed in any way within the each loop, this will not affect the items it refers to and processes.

## for (loop)

The for..from..to..step.. construct specifies looping through a sequence of integer values with a given increment.

The for loop counter variable (which counts from 0) is of Type Int and does not have to have been defined in a variable statement.

The three defining values, from, to, and step, must all be of Type Int, positive or negative. and may be defined by literals, variables or expressions that evaluate to integers.

Note that, if you require a negative step value, then the literal, variable, or expression must start with a negative sign. This is needed at compile time to determine the nature of the exit condition. So if you have a variable s that holds a negative value to be used to step in reverse order, then you would write:

variable names name set to ["Tom", "Dick", "Harriet"]expression variable s name set to -1 expression +for n variableName from 2 expression to 0 expression step -(-s)expression print names[n]expression end for

## if

The if statement specifies which of several code sequences is to be executed next.

- It is structured like this:
  if condition then
  statements
  else if condition then
  statements
  else
  statements
  end if
- The conditions are Boolean values or expressions.
- else if and else clauses are optional.
- You can add as many else if clauses as you wish, but only one unconditional else which, if present, must be last.

See also if expression for using the related if to return a value.

#### Examples using if statement

▶ Simple choice between equality and inequality:

+if head is apple condition then call setAppleToRandomPosition procedureName(apple, body arguments) else if call body.removeAt procedureName(0 arguments) end if

▶ Choice between an equality, a Boolean and the alternative:

+if item is value condition then set result variableName to true expression else if item.isBefore(value)condition then set result variableName to binarySearch(lst[..mid], item)expression else if set result variableName to binarySearch(lst[mid + 1..], item)expression end if

## let

The let statement may be thought of as being between a constant definition and a variable's set statement.. Like a variable set a let may be used only within a routine, but unlike a variable its value may not be changed with a set. It is recommended that you always use a let in preference to a variable unless you need to be able to assign a new value to it.

You can put a let in a loop, so the variable gets a new value each time it is executed, but the value of the variable cannot be changed any other way.

## print

The print instruction sends text strings to the Display pane. For example:

- print "Hello"expression ⟶ Hello
- let a name be 3 expression let b name be 4 expression print a*b expression ⟶ 12
- print "{a} times {b} equals {a*b}"expression ⟶ 3 times 4 equals 12

The last example above uses interpolated strings. Arguments placed within curly braces are evaluated before printing, and these may be separated by literal text and punctuation as needed. This is one recommended way to print more than one value on a line. The other way is to use print procedures.

If the print instruction is used without any following expression, then it prints a newline, i.e. its effect is the same as print "\n".

## repeat (loop)

The repeat..end repeat when condition loop is used when you want at least one execution of the enclosed statements followed by a test that chooses either to execute them again or to exit from the loop.

condition is either a Boolean variable or an expression that evaluates to a Boolean value.

If condition is true then the loop ends, if false it resumes. For example:

+repeat call file.writeLine(names[i]) set i to i + 1 end repeat when i is names.length()

## set

The set statement is used to assign a new value to an existing variable. The new value must be of the same Type as (or a Type compatible with) that of the variable. A set statement may not assign a new value to a parameter within a procedure unless it the parameter is preceded by out in the parameter list.

## throw (exception)

You can deliberately generate, or 'throw', an exception when a specific circumstance is identified, using a throw statement which defines an explanatory string. An example:

+if value > maximum then throw exception "proportion exceeds 100%" else set r to value/maximum*r end if

At runtime, if the condition is met, execution will stop and the Display will show:

A Runtime error occurred in the Elan code
Error: proportion exceeds 100%

This example that puts current values into the exception message string:

+if yp is 0 then throw exception "yp is zero: {xp},{yp} + {xq},{yq}" end if

For the program to retain control when an exception is raised, put the code that may cause the exception into a try statement, where you can catch (i.e. receive) the exception message string, and continue as appropriate.

## try (test)

You can test whether another piece of code might throw an exception by wrapping it in a try statement. This might arise when calling a System method that is dependent upon external conditions, for example:

+try call foo procedureName() print "not caught"expression +catch exception in e variableName print e expression end try The variable holding the exception (by default named e, but this may be changed by you) is of Type String. You can compare the exception message to one or more expected messages and, if the message does not match an expected exception, you may choose to throw the exception 'up', as in this example: +try call foo procedureName() print "not caught"expression +catch exception in e variableName +if e isnt "an expected message"condition then throw exception e message end if end try

## variable

The variable statement defines the name to be used to store mutable (i.e. changeable) data. The name defined must be a valid identifier, and the initial value is given by a following expression. For example:

variable note name set to 440 expression

## while (loop)

The while condition..end while loop is used when you want execution of the enclosed statements to begin only if condition is true.

condition is either a Boolean variable or an expression that evaluates to a Boolean value.

When condition is true the enclosed code is executed; when false the loop is bypassed. For example:

+while not file.endOfFile() print file.readLine() end while

# Expressions

One of the most important constructs in programming is the expression. An expression evaluates and returns a value using the following elements:

- Literal value
- Named value
- Operator (including brackets)
- function reference
- lambda

## Literal value

A literal value is where a value is written 'literally' in the code, such as 3.142 – in contrast to a value that is referred to by a name.

Here is a table showing some example literal values. Follow the links for more information about each Type.

Type | Example of literal
--- | ---
Int | 3
Float | 2.0
Boolean | true
String | "Hello"
Tuple | tuple(3, "banana")
List | ["lemon", "lime", "orange"]
Dictionary | ["a":3, "b":5]
ListImmutable | {"lemon", "lime", "orange"}
DictionaryImmutable | {"a":3, "b":5}

For more about List and Dictionary literals see the Standard (mutable) data structures table.
For more about ListImmutable and DictionaryImmutable literals see the Immutable data structures table.

## Scoping and name qualification

With the exception of constant and enum, which are global in scope, a named values is always local: i.e. its use is confined to the method in which it is defined.

Local named values can have the same name as a constant, function, or procedure defined at global level or defined in the standard library. In such cases, when the name is used within the same method, then it will refer to the local definition. If you have done this, but then need to access the constant, function, or procedure with the same name, then you can simply prefix the use of the name with a qualifier of either global. or library. as appropriate.

## Indexed values

If a variable is of an indexable Type, then an index or index range may be applied to the variable within an expression. For example:

        variable a set to "Hello World!"
        print a[4]        ⟶ o
        print a[4..]    ⟶ o World!
        print a[..7]    ⟶ Hello W   (since the upper bound of a range is exclusive)
        print a[0..4]  ⟶ Hell         (for the same reason)

In the examples above, the result is of Type String in all cases.

When using indexing on other Types:

- with a single index, the result is of the same Type as the elements of the data structure being indexed
- with an index range, the result is of the same Type as the data structure itself

Indexable Types are String, Array, Array2D, List, Dictionary, ListImmutable and DictionaryImmutable.

Index ranges cannot be applied to Dictionary or DictionaryImmutable.

If the index values in a range are equal, or the second is smaller than the first, then an empty data structure of the correct Type is generated.

Unlike in many languages, indexes in Elan (whether, single, multiple, or a range) are only ever used for reading values. Writing a value to a specific index location is done through a method such as in these examples:

        put           on a   List
        withPut   on a    ListImmutable
        put           on a    Dictionary
        withPut   on a    DictionaryImmutable

## Operators

#### Arithmetic operators

Arithmetic operators can be applied to Float or Int arguments. The result may be a Float or an Int depending on the arguments.

For ^ + - *, the result is a Float if either of the arguments is a Float, and an Int if both arguments are Int.

For /, the result is always a Float. It can be converted to an Int using the floor() function.

mod and div only operate on Int arguments, and the result is Int.

        2^3             ⟶  8
        2/3             ⟶  0.666..
        2*3             ⟶  6
        2 + 3         ⟶  5
        2 - 3         ⟶  -1
        11 mod 3   ⟶  2 (integer remainder)
        11 div 3   ⟶  3 (integer division)

Arithmetic operators follow the conventional rules for precedence i.e. 'BIDMAS' (or 'BODMAS').

When combining div or mod with any other operators within an expression, insert brackets to avoid ambiguity e.g.:

        (5 + 6) mod 3

Note that mod is more of a remainder operator than a modulus operator -- the result takes the sign of the first argument. If both arguments are positive, there is no difference.

The minus sign may also be used as a unary operator, and this takes precedence over binary operators so:

        2*-3           ⟶  -6

Note that the editor automatically puts spaces around the + and – binary operators, but not around ^, / or *. This is just to visually reinforce the precedence.

The div operator rounds its result down, as if with the floor function. This can give unexpected results with negative numbers, for example:

        11 div -3   ⟶  -4

The + operator is also used for concatenating Strings. See String.

#### Logical operators

Logical operators are applied to Boolean arguments and return a Boolean result.

and and or are binary operators
not is a unary operator.

The operator precedence is not → and → or, so this example, which implements an 'exclusive or', need not use brackets and can rely on the operator precedence:

+function xor name(a as Boolean, b as Boolean parameter definitions) returns Boolean Type return a and not b or b and not a expression end function

#### Equality testing

Equality testing uses the is and isnt keywords with two arguments. The arguments may be of any Type.

- a is b returns true, if a and b are both of the same Type and their values are equal. The only exception is that if one argument is of Type Float and the other is of Type Int, then is will return true if their values are the same, i.e. are the same whole number.
- isnt returns the opposite of is.
- Where a binary operator is expected, as soon as you type is the editor will automatically insert a space after it. To enter isnt you need to delete the space (using the Backspace key) and then type nt.

Note that in Elan equality testing is always 'equality by value'; there is no such thing as 'equality by reference'.

If the items being compared are composite Types, the elements within them are compared sequentially to see if the objects are equal. For example two distinct instances of the same class compare equal if the values of all their properties compare equal. And two Lists compare equal if they contain the same elements in the same order.

The compiler rejects any attempt to compare instances of different classes unless abstract classes and inheritance are involved. Two instances which are subclasses of the same abstract class compare equal only if they are of the same class (and have the same property values).

#### Numeric comparison

The numeric comparison operators are:

        >         for     greater than
        <         for     less than
        >=        for     greater than or equal to
        <=        for     less than or equal to

Each is applied to two arguments of Type Float, but any named value or expression that evaluates to an Int may always be used where a Float is expected.

#### Notes

- These operators cannot be applied to strings. Use the dot methods isBefore and isAfter to compare strings alphabetically. See Dot methods on a String.
- Where a binary operator is expected, as soon as you type < or > the editor will automatically insert a space after it. To enter <= or >= you need to delete the space (using the Backspace key) and then type =.

#### Combining operators

You can combine operators of different kinds, e.g. combining numeric comparison with logical operators in a single expression. However the rules of precedence between operators of different kinds are complex. It is strongly recommend that you always use brackets to disambiguate such expressions, for example:

(a > b) and (b < c)expression
(a + b) > (c - d)expression

## Function reference

An expression may simply be a reference to a function, or it may include several function references within it. Examples: print sinDeg(30)expression variable x name set to sinDeg(30)^2 + cosDeg(30)^2 expression variable name name set to inputString("Your name")expression print name.upperCase()expression

#### Notes

- The third example above is not strictly a function call, but is a 'system method' call. System methods may be used only within the main routine or a procedure, because they have external dependencies or side effects.
- In the fourth example, upperCase is a dot method that may be applied to any instance (variable or literal) of Type String. See Dot methods on a String.

## lambda

A lambda is a lightweight means to define a function 'in line'. You typically define a lambda:

- If the functionality it defines is needed in only one location: typically for a particular call to a Higher-order Function (HoF).
- If you need to capture a local variable in the implementation. This is called 'closing around a variable'.

The syntax for a lambda is as follows:

- Start with the keyword lambda.
- Parameter definitions, comma-separated, follow the same form as parameter definitions in a function or procedure, but without surrounding brackets.
- The => symbol, which is usually articulated as 'returns', 'yields' or even 'fat arrow'.
- An expression that makes use of the parameters, and may also make use of other variables that are in scope.

Example: +function liveNeighbours name(cells as List<of Boolean>, c as Int parameter definitions) returns Int Type let neighbours name be neighbourCells(c)expression let live name be neighbours.filter(lambda i as Int => cells[i])expression return live.length()expression end function Notes:

- Although a lambda is commonly defined 'inline' (as shown above) it is possible to assign a lambda to a variable and hence to re-use it within the scope of that variable.

## if expression

The if expression has a similar structure to the if statement, but is an expression that returns a value.

- The conditions are Boolean values or expressions.
- It specifies which of several expressions is to be evaluated to give the returned value.
- else if and else clauses are optional.
- You can add as many else if clauses as you wish, but only one unconditional else which, if present, must be last.
- Though structured like the if statement, it is edited in one field rather than in separate frames. On moving away from editing an if expression, it is automatically reformatted to be similar to an if statement, though without indentation. This means that an if expression should usually be kept simple.
- An if expression may be used within an if expression but will be hard to read even if it is enclosed in brackets.

#### Examples using if expression:

▶ Choice in set and let assignments (the brackets around the first if expression are optional):

variable c set to 1160 set c to (if c < 1160
then c + 40
else c - 1160) let d be if c < 580
then c - 40
else c + 60

▶ Choice in a return statement:

return if isGreen(attempt, target, n)
then setChar(attempt, n, "*")
else attempt

▶ Using else if clause:

return if attempt[n] is "*"
then attempt
else if isYellow(attempt, target, n)
then setChar(attempt, n, "+")
else setChar(attempt, n, "_")

## new

A 'new instance' expression is used to create a new instance of a library data structure, or a user-defined class or record – either to assign to a named value, or as part of a more complex expression. Example of use from demo program snake_PP.elan:

let blocks name be new Array2D<of Int>(40, 30, white)expression

Where the new instance is of a user-defined class or record the expression may optionally be followed by a with clause in order to specify any property values. Example of this use:

+function dealCard name(random as Float parameter definitions) returns Card Type let number name be (random*52).floor()expression let rank name be rankValue.keys()[number div 4]expression let suit name be number mod 4 expression return new Card() with
rank set to rank,
suit set to suit expression end function

## copy..with

A copy..with expression is used to make a copy of an existing instance, but with a different value for one or more of the properties – either to assign to a named value, or as part of a more complex expression.

It is used extensively within functional programming where you are dealing with records or other immutable Types. Example of use in this manner, taken from demo program snake_FP.elan:

+function newApple name(g as Game parameter definitions) returns Game Type let x, rnd2 name be g.rnd.nextInt(0, 39)expression let y, rnd3 name be rnd2.nextInt(0, 29)expression let apple2 name be newSquare(x, y)expression let g2 name be copy g with
apple set to apple2,
rnd set to rnd3 expression return if bodyOverlaps(g2, apple2) then newApple(g2)
else g2 expression end function

copy..with may also be used in object-oriented programming with instances of regular (mutable) classes. Also, note that a with clause (following the same syntax as in a copy..with expression), may be used within a new instance expression to set up properties for the object not specified in the constructor.

## empty

An 'empty of Type' expression is used to make the default (empty) instance of any Type – usually only created for comparing to another instance to test whether that other instance is also empty or default. This may arise, for example, if a class or record is defined with a property that has never had a value assigned to it. The following example is taken from demo program pathfinder.elan:

+procedure visitNextPoint name(parameter definitions parameter definitions) call updateNeighbours procedureName() set property.current variableName to nextNodeToVisit()expression +if (property.current is empty Node) or (property.current.point is property.destination)condition then set property.running variableName to false expression else if call property.current.setVisited procedureName(true arguments) end if end procedure

It is also possible explicitly to set a property or a named value to an empty instance of the appropriate Type.

# Comments

## # comment

A comment is not an instruction: it is ignored by the compiler and does not change how the program works. Rather, a comment contains information about the program, intended to be read by a person seeking to understand or modify the code.

Every comment starts with the hash symbol # followed by some text or a blank line. The text field in a comment may contain any text, except that it must not start with the open square bracket symbol [.

Comments may be inserted at any level: in the Global, Member, or Statement instruction levels, as well as from the new code prompt – every prompt provides a # for entering a comment.

Every Elan program has a single comment at the top of the file, which is generated by the system and cannot be edited or deleted by the user. This comment is known as the 'file header' and shows the version of Elan being run.

# Compile errors and warnings

Q: What is the difference between a compile error and a warning.

A: A warning usually indicates that the fix may involve adding some more code, for example adding a definition for an unknown identifier. An error usually indicates that you will need to alter code to fix the error. But they are similar in that you will not be able to run your program until the issues are fixed. In all programming languages it is a good practice 'treat all compile warnings as errors' i.e. fix them as soon as you see them appear.

## Messages

### Expression must be ...

An expression, when evaluated, results in a value of a Type that is not compatible with its 'target', for example: if the result of the expression is being assigned to an existing variable, or if an expression is defined 'inline' as an argument into a method call.

### Cannot use 'this' outside class context

The keyword this may only be used within an instance method on a class to refer to the current instance.

### Abstract Class ... must be declared before it is used

If a class inherits from one or more abstract classes, then the latter must all have already been declared (defined) earlier in the code file.

### Member ... must be of type ...

This error occurs when a class is defined as inheriting from an abstract class, and has implemented an inherited member (method or property) with the correct name, but with different Types.

### Incompatible types. Expected: ... Provided: ...

### Cannot determine common type between ... and ...

### ... is not defined for type ...

Arises when 'dot calling' a member (method or property) that does not exist on the Type of the named value or expression before the dot.

### Cannot call a function as a procedure

A function (or function method) is to be used within an expression, not via a call instruction.

### Cannot use a system method in a function

A 'system method' (defined in the Standard Library) returns a value like a function does. However, because a system method either makes changes to the system and/or depends on external inputs, it may be used only within a procedure or the main routine.

### Code change required ...

Indicates that a library method or class has been changed since the version in which your Elan code was written. The link in the message should take you directly to information in the Library Reference documentation on how to update your code to cope with the change.

### Cannot call procedure ... within an expression

A procedure may be used only within a call instruction.

### Cannot invoke ... as a method

The code is attempting to use a free-standing method (function or procedure) as a 'dot method' on a named value or the result of an expression.

### Cannot ...index ...

An index (in square brackets) may be applied only to certain data structure Types: String, Array, Array2D, List, ListImmutable, Dictionary, and DictionaryImmutable.

### Cannot range ...

A range may be applied only to certain data structure Types: String, Array, List, and ListImmutable.

### Cannot new ...

The Type specified after the call keyword cannot be instantiated. Either the Type is unknown, or it is an abstract class or interface.

### Source for 'each' must be an Array, List, or String.

### Superclass ... must be inheritable class

A concrete class may inherit from an abstract class, and/or an interface, but not from another concrete class. In Elan, all classes must be either abstract or 'final' – a final class being concrete and not-inheritable.

### Superclass ... must be an interface

An interface may inherit from other interfaces, but not from any class.

### Class/interface ... cannot inherit from itself

The message is self explanatory.

### There must be only one abstract superclass ...

A class may inherit from only one abstract class. However, it may additionally inherit from one or more interfaces.

### Cannot reference private member ...

A private member (method or property) may be accessed only by code within the class, or within subclasses of it. It may not be accessed by any code outside the class hierarchy.

### ... must implement ...

If a concrete class inherits from any abstract class or interface(s) it must implement all abstract methods defined in those Types.

### ... must be concrete to new

You cannot create an instance of any abstract class or interface: only of a concrete class.

### Cannot pass ... as an out parameter

If a parameter of a procedure is marked with out then this means that the parameter may be reassigned within the procedure. Therefore you must pass in a variable that can be re-assigned. You cannot pass in: a constant, a literal value, or a named value that is defined by a let instruction.

### Cannot call extension method directly

A method that is defined within the Library as an extension method, such as asString, may be called on a named value or an expression only using dot syntax.

### Cannot prefix function with 'property'

The prefix property. may only be used before a property name: not a function name.

### Missing argument(s) ...

The method being called expects more arguments than have been provided.

### Too many argument(s) ...

A method has been passed more arguments than it expects.

### Argument types ...

One or more arguments provided to the method are of the wrong Type.

### ...<of Type>...

Certain data structure Types, including Array, Array2D, List must specify the Type of their members, for example List<of Int>. Failure to specify the '<of Type>' on these Types will give an error, as will specifying 'of Type' where it is not required. Dictionaries require Types to be specified for both the keys and the values, for example: Dictionary<of String, Float>.

### May not re-assign the ...

Attempting to re-assign, or mutate, a named value that may not be re-assigned in the current context.

### Name ... not unique in scope ...

Attempting to create an identifier with the same name as one already defined within the same scope.

### May not set ... in a function

A property can be re-assigned only within a procedure method, not within a function, because re-assigning a property is a 'side effect'.

### The identifier ... is already used for a ... and cannot be re-defined here.

An existing named value may not be defined again within the same scope.

### Duplicate Dictionary key(s)

Attempting to define a literal Dictionary or DictionaryImmutable with one or more duplicated keys in the definition.

### To evaluate function ... add brackets ...

If you intend to evaluate a function, the function name must be followed by brackets even if the function defines no parameters. If your intent was to define a reference to the function (a pattern used commonly in 'Functional Programming' then the name of the function must be preceded by the keyword ref and a single space.

### Library or class function ... cannot be preceded by by 'ref'

The keyword ref may be applied only to functions that you have defined in your own code as a standalone (global) function. It may not be applied to a function method defined on a class, nor to a library function. (You may, however, define your own function that simply delegates its implementation to a library function.)

### a comment may not start with [ unless it is a recognised compiler directive

Compiler directives are a planned future capability. They will look like comments, but begin with an open square bracket. To avoid the possibility of ambiguity, you may not start your own comments with an open square bracket.

### Condition of 'if' expression does not evaluate to a Boolean.

### Cannot have any clause after unconditional 'else'.

### ... is a keyword, and may not be used as an identifier.

An Elan keyword cannot be used to as the name for a value, property, or method. Try shortening the name, lengthening it, or using a different name

For reference, the complete list of keywords is:
#, abstract, and, as, assert, be, call, catch, class, constant, constructor, copy, div, each, else, empty, end, enum, exception, for, from, function, if, image, in, inherits, interface, is, isnt, lambda, let, main, mod, new, not, of, or, out, print, private, procedure, property, record, ref, repeat, return, returns, set, step, test, then, this, throw, to, try, variable, while, with

### ... is a reserved word, and may not be used as an identifier.

In addition to Elan keywords there are certain other 'reserved words' that cannot be used to define the name for a value, property, or method.If you encounter this error you may eliminate the error simply by adding more valid characters to the name – for example just by changing case to case_.

Why are there any reserved words that are not Elan keywords? There are three kinds of reserved word:

- Potential keywords that might be added to Elan in future releases.
- Lowercase versions of Elan Type names, such as int, float, string, boolean, array, list, dictionary. It is not considered good practice to use Type names for identifiers: instead you should give each identifier a name that indicates what it does (for a method), or represents (for a named value).
- Words that are keywords in JavaScript (but not in Elan). Elan compiles to JavaScript and use of these words as identifiers could cause errors. (If you are asking, 'Why doesn't the Elan compiler just obfuscate these?' the answer is that we evaluated that option but concluded that it added a lot of complexity – especially in regard to debugging and interaction with the Elan editor – just to eliminate the minor inconvenience of having to extend the word so as to make your identifier distinct.)

For reference, the complete list of reserved words is:
action, arguments, array, async, await, boolean, break, by, byte, case, char, const, continue, curry, debugger, default, delete, dictionary, do, double, eval, export, extends, final, finally, float, goto, ignore, implements, import, instanceof, int, into, list, long, match, namespace, native, null, on, optional, otherwise, package, partial, pattern, protected, public, short, static, string, super, switch, system, synchronized, throws, todo, transient, typeof, void, volatile, var, when, yield

### Index cannot be negative.

An index into an array or list cannot have a negative value. If a negative is given in literal form e.g. a[-3] then this will generate a compile error. If you use a named value for an index and it is negative, then this will cause a runtime error.

### Cannot do equality operations on Procedures or Functions.

It is not possible to apply comparison operations to functions (with or without the ref keyword prefix) or procedures as themselves. It is, however, possible to compare the results of two function evaluations. It is possible that you are seeing this message because you intended to evaluate a function but forgot to add the brackets after the name.

### Property ... is not of an immutable type.

Properties on a record may only be of immutable Types.

### ... cannot be of mutable type ...

Element Type for a ListImmutable must itself be an immutable Type. Similarly, for an DictionaryImmutable the Types for both the key and the value must be immutable ones.

### ... cannot have key of type ...

The Type of the key for any dictionary Dictionary must be an immutable Type, and not itself an indexable Type.

### No such property ... on record ...

The property name given in the record deconstruction does not match a property on the given Type of record.

### Cannot discard in record deconstruction ...

### Wrong number of deconstructed variables.

### referencing a property requires a prefix.

If you are referring to a property of a class from code defined within the class then the property name must be preceded by property.

### 'out' parameters are only supported on procedures.

You cannot defined an out parameter in a function (because that would imply the possibility of creating a side effect).

### There can only be one 'main' in a program.

### Unsupported operation.

You cannot chain two 'unary' operators (those that apply to a single value), such as - or not successively within an expression.

### Parameter ... may not have the same name as the method in which it is defined.

A function or procedure named e.g. 'foo' may not define a parameter with that same name.

## Field help

#### 'arguments' field in a call instruction

An argument list passed into a function or procedure call, must consist of one or more arguments separated by commas. Each argument may in general be any of:

- A literal value
- A named value
- An expression

In certain very specific contexts, however, some options are disallowed by the compiler.

### 'computed value' field in an assert instruction

The 'actual' field should be kept as simple as possible, preferably just a named value or a function evaluation. Generally, if you want to use a more complex expression, it is better to evaluate it in a preceding let instruction and then use the named value in the 'actual' field of the assert instruction. Some more complex expressions are permissible, but these two restrictions apply:

- Any expression involving a binary operator such as +, isnt, etc., must have brackets around it.
- You may not use the is operator within the 'actual' field, because the parser will confuse this with the is keyword that is part of the assert instruction.

### 'variable name' field in a set instruction

The first field in a set instruction most commonly takes the name of an existing variable. It may, however, may also take the following forms:

- Within a class it may take the form property.name to set the property name
- A tuple deconstruction. See Tuple
- A list deconstruction. See List

### 'comment' field

### literal value or data structure in a constant

The value of a constant must be a literal value of a Type that is not mutable. This can be a simple value (e.g. a number or string), or an immutable List or Dictionary.

### 'values' field in an enum definition

enum values must each be a valid identifier, separated by commas.

### 'message' field in a throw instruction

An exception message must be either a literal string or a named value holding a string.

### expression field - used within multiple instructions

This field expects an expression. For the various forms of expression see Expressions.

### identifier field - used within multiple instructions

### 'if' field in an else clause

### 'inherits ClassName' field in a class

An inheritance clause, if used, must consist of the keyword inherits followed by a space and then one or more Type names separated by commas.

### 'name' field in a function or procedure definition

A method name must follow the rules for an identifier.

### 'parameter definitions' in a function or procedure definition

Each parameter definition takes the form:

name as Type

The name must follow the rules for an identifier.

The Type must follow the rules for a Type.

If more than one parameter is defined, the definitions must be separated by commas.

### 'procedureName' in a call statement

Valid forms for a procedure call are

- call procedureName()
- call instanceName.procedureMethodName()
- call property.propertyName.procedureMethodName()
- call library.procedureName()

The last one is used only if there is a need to disambiguate between a library procedure and a user-defined (global) procedure with the same name.

### 'Type' field in a function or property definition

For certain Types the name may be followed by an of clause, for example:

List<of Int>
Dictionary<of String, Int>

### 'Name' field in a class or enum definition

Type names always begin with a capital letter, optionally followed by letters of either case, numeric digits, or underscore symbols. Nothing else.

### 'name' field in a let or variable instruction

The definition for a variable or for a let statement is most commonly a simple name. Less commonly, it may take the form of: tuple deconstruction, list deconstruction, or record deconstruction.

# Runtime errors

## Messages

### Tests timed out and were aborted

An error or infinite loop found in a test. Refer to ghosting tests.

### Overly complex expressions

Overly complex expressions – for example involving a sequence of open brackets – can result in very slow parsing. We strongly recommend that you you simplify the contents of this field, for example by breaking out parts of it into separate let statements; otherwise it might become impossible to add more text.

### ReferenceError: Cannot access '[name]' before initialization

Elan Language Reference go to the top