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:
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.
The Types used in the language are here listed, each linking to its definition and further details:
| Type group | Types | ||||
|---|---|---|---|---|---|
| Fundamental | Integer |
Floating point |
Truth value |
Character string |
|
| Mutable data structures |
List |
Look-up dictionary |
Simple array |
2-dimensional array |
|
| Immutable data structures |
Immutable list |
Immutable look-up dictionary |
Set |
LIFO stack |
FIFO queue |
| Data I/O | Text file input |
Text file output |
|||
| Graphics | Turtle | SVG vector graphics | |||
| User-defined | Enumeration |
Record |
Class |
Abstract class |
Interface |
| Other | Random number |
Function reference |
|||
Named values are statically typed: their Type, once defined, cannot be changed. A named value's Type is:
parameters of functions and procedures
The properties or contents of a named value that is of an immutable Type may not be changed directly. You can, however, create another instance that is a copy of the original, with all the same property values except for specific changes that you want to make. The newly-minted copy (with changes) may be assigned either to a new named value, or as a re-definition of the original named value.
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 ' | mutate | scope | |
| ✘ | ✔ | ✘ | ✘ | global | constant values are set only at compile time | |
| ✔ | ✘ | ✔ | local | |||
| ✔ | ✘ | ✘ | local | |||
| ✔ | ✔ | ✔ | ✔ | local | ||
| parameter | ✔ | ✘ | ✔ | local | formal input argument of function, procedure or lambda | |
| parameter | ✔ | ✘ | ✘ | local | formal input argument of function, procedure or lambda | |
| ✔ | ✔ | ✔ | local | formal output argument of procedure | ||
| ✔ | ✔ (see note) | ✔ | local | formal output argument of procedure an if the actual argument is defined with | ||
The name given to every named value must follow the rules for an identifier:
Every Type, whether system (e.g.
'Scope' in the table above refers to where in your code a named value is accessible.
Local named values can have the same name as a
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:
A program must have a
The
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
▶ A
A procedure is a named piece of code that can define parameters which are given inputs via arguments in a
Unlike a function:
Therefore the statements within a procedure can:
▶ Calling a procedure:
▶ Using keyword
A
Unlike a procedure:
A
You follow the
▶ The function definition
▶ Reference to the function
The function example above includes two uses of dot syntax to qualify items in an expression:
Dot syntax is also used:
The procedure called may be defined in a class or provided by the system.
You cannot, however, use dot syntax with your user-defined procedures at the global level,
nor with some system provided functions (e.g.
A
Without having to run your program, the
▶ Test function
When testing
If the expression you are testing would cause a runtime error then the error will be displayed in the red fail message:
If there are failures, mark the tests that you added since the last successful test as
In the last
If you have a test that compares strings longer than 20 characters, any test failure message will be reduced to reporting the first character at which the actual (computed) and expected strings differ.
▶ Reporting the first character at which the actual (computed) and expected strings differ:
To ignore tests, use
Even when
▶ Ghosting an entire test:
▶ Ghosting an assert:
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
A
Note that a
However a
Note that you are not required to provide a value for each property because, where a property is not specified in the
You can then read the values from the properties using dot syntax for example:
Or even to the same name if that name is a
This last example shows how you enter the comma-separated
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:
A record may be deconstructed, meaning that its properties are read into separate
will read the
A
Unlike a
Like any other Type its name must begin with an uppercase letter.
Here is an example of
A class may define:
A
The created instance may then be used within expressions, like any other named value.
Here, method
An ordinary class (also known as a concrete class) may optionally inherit from just one
An
As with a concrete class, any of these members may be made
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
If you wish to have several subclasses of an
An
An
Important: An
See also: Inheritance.
▶ Example from a version of program
A
A constant is defined by a literal of an immutable Type, namely
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 re-define a system constant such as
Examples of literal definitions of the valid Types of constant:
An
A reference to an
Reference to the value in an enum is by using dot syntax
An
Examples of definition and use:
And from demo program
Member instructions (also referred to simply as 'members') are located within an
This table shows which kinds of property, function or procedure, and constructor are applicable to the various kinds of class.
| Abstract | Concrete | notes | |||
|---|---|---|---|---|---|
| ✔ | ✔ | ||||
| ✔ | ✔ | prototype for function reference only | |||
| ✔ | ✔ | prototype for procedure call only | |||
| ✔ | ✔ | ✔ | |||
| ✔ | ✔ | ✔ | |||
| ✔ | ✔ | ||||
| ✔ | ✔ | ||||
| ✔ | ✔ | ||||
| ✔ | |||||
| ✔ | |||||
An abstract property may be defined only on an
An abstract function method may be defined only on an
An abstract procedure method may be defined only on an
A
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
A property may be read, but not written to. Properties may be modified only from outside the class by means of a Procedure method.property names List ... if names is empty List
A function method follows the same syntax and rules as a global function. The differences are:
A 'procedure method' follows the same syntax and rules as a global procedure. The differences are:
A
A (concrete) class may have an optional
If a class does define a constructor, and the constructor defines parameters, then when the class is instantiated (using
then it may be instantiated like this:
Statement instructions (also referred to simply as 'statements') are the imperative keywords used in the methods (procedural logic) of a program, and are:
The
Some programming languages have a feature for making assertions while your program is running.
In Elan, you can get equivalent functionality by throwing an
A
The procedure may be:
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,
If the parameter is not an
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.
There is a limit to the complexity of a
The
The
▶ To print a
▶ Function to reverse a
▶ The
The
The
The three defining values,
Note that, if you require a negative step value, then the literal,
The
See also
▶ Simple choice between equality and inequality:
▶ Choice between an equality, a Boolean and the alternative:
The
You can put a
The
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
The
If
The
You can deliberately generate, or 'throw', an exception when a specific circumstance is identified, using a
At runtime, if the condition is met, execution will stop and the Display will show:
This example that puts current values into the exception message string:
For the program to retain control when an exception is raised, put the code that may cause the exception into a
You can test whether another piece of code might throw an exception by wrapping it in a
The
The
When
One of the most important constructs in programming is the expression. An expression evaluates and returns a value using the following elements:
A literal value is where a value is written 'literally' in the code, such as
Here is a table showing some example literal values. Follow the links for more information about each Type.
| Type | Example of literal |
|---|---|
| Int | |
| Float | |
| Boolean | |
| String | |
| Tuple | |
| List | |
| Dictionary | |
| ListImmutable | |
| DictionaryImmutable |
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.
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
When using indexing on other Types:
Indexable Types are
Index ranges cannot be applied to
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
Arithmetic operators can be applied to
For the
For the
The operator
The operator
2^3 ⟶ 8
2/3 ⟶ 0.666..
2*3 ⟶ 6
2 + 3 ⟶ 5
2 − 3 ⟶ −6
11 mod 3 ⟶ 2 (integer remainder)
11 div 3 ⟶ 3 (integer division) is deprecated, use instead:
(11/3).floor() ⟶ 3 , and note that rounding is always down:
(-11/3).floor() ⟶ −4
Arithmetic operators follow the conventional rules for precedence i.e. 'BIDMAS' (or 'BODMAS').
When combining
(5 + 6) mod 3
Note that
The minus sign may also be used as a unary operator, and this takes precedence over binary operators so:
2*−3 ⟶ −6
The editor automatically puts spaces around the operators
The
The editor automatically inserts spaces around the
The
Logical operators are applied to
The operator precedence is
Equality testing uses the
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).
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
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
The syntax for a
The
▶ Choice in
▶ Choice in a
▶ Using
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
Where the new instance is of a user-defined class or record the expression may optionally
be followed by a
A
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
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
It is also possible explicitly to set a property or a named value to an empty instance of the appropriate Type.
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
Comments may be inserted at any level: in the Global,
Member, or Statement instruction levels,
as well as from the
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.
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.
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.
The keyword
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.
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.
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.
A function (or function method) is to be used within an expression, not via a
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.
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.
A procedure may be used only within a
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.
An index (in square brackets) may be applied only to certain data structure Types:
A range may be applied only to certain data structure Types:
The Type specified after the
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.
An interface may inherit from other interfaces, but not from any class.
The message is self explanatory.
A class may inherit from only one abstract class. However, it may additionally inherit from one or more interfaces.
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.
If a concrete class inherits from any abstract class or interface(s) it must implement all abstract methods defined in those Types.
You cannot create an instance of any abstract class or interface: only of a concrete class.
If a parameter of a procedure is marked with
A method that is defined within the Library as an extension method, such as
The prefix
The method being called expects more arguments than have been provided.
A method has been passed more arguments than it expects.
One or more arguments provided to the method are of the wrong Type.
Certain data structure Types, including
Attempting to re-assign, or mutate, a named value that may not be re-assigned in the current context.
Attempting to create an identifier with the same name as one already defined within the same scope.
A property can be re-assigned only within a procedure method, not within a function, because re-assigning a property is a 'side effect'.
An existing named value may not be defined again within the same scope.
Attempting to define a literal
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.
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:
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
Why are there any reserved words that are not Elan keywords? There are three kinds of reserved word:
For reference, the complete list of reserved words is:
An index into an array or list cannot have a negative value. If a negative is given in literal form e.g.
It is not possible to apply comparison operations to functions or procedures as themselves. It is, however, possible to compare the results of two function evaluations. You may see this message because you intended to evaluate a function but forgot to add the brackets after the name.
Properties on a record may only be of immutable Types.
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.
The Type of the key for any dictionary Dictionary must be an immutable Type, and not itself an indexable Type.
The property name given in the record deconstruction does not match a property on the given Type of record.
If you are referring to a property of a class from code defined within the class then the
property name must be preceded by
You cannot defined an
You cannot chain two 'unary' operators (those that apply to a single value), such as
A function or procedure named e.g. 'foo' may not define a parameter with that same name.
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:
In certain very specific contexts, however, some options are disallowed by the compiler.
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
The first field in a
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.
An exception message must be either a literal string or a named value holding a string.
This field expects an expression. For the various forms of expression see Expressions.
An inheritance clause, if used, must consist of the keyword
A method name must follow the rules for an identifier.
Each parameter definition takes the form:
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.
Valid forms for a procedure call are
For certain Types the name may be followed by an
Type names always begin with a capital letter, optionally followed by letters of either case, numeric digits, or underscore symbols. Nothing else.
The definition for a variable or for a
An error or infinite loop found in a test. Refer to ghosting tests.
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
Elan Language Reference go to the top