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 Elan, 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, assist in the modularisation of code and reduce errors.

Both kinds of named value must be initialised with literals, expressions or initialisation methods.

Loop counter variables, however, may be defined implicitly in a for instruction.

Data Types

There are two fundamentally different kinds of data Type, differing by how their values are passed around in a running program:

This table lists the value Types and the standard data structure Types in Elan, each linking to its definition and further details:

Type groupValue TypesReference Types
Value data
Integer
Int int int Integer int
Floating point
(Real) number

Float float double Double double
Truth
value

Boolean bool bool Boolean bool
Standard
data structures
Character string
String str string String String
Regular expression
RegExp RegExp RegExp RegExp RegExp
List
List list List List List
Look-up dictionary
Dictionary Dictionary Dictionary Dictionary Dictionary
Standard
data structures
Set
HashSet HashSet HashSet HashSet HashSet
LIFO stack
Stack Stack Stack Stack Stack
FIFO queue
Queue Queue Queue Queue Queue
Graphics Turtle graphics
Turtle Turtle Turtle Turtle Turtle
SVG vector graphics
CircleVG CircleVG CircleVG CircleVG CircleVG
ImageVG ImageVG ImageVG ImageVG ImageVG
LineVG LineVG LineVG LineVG LineVG
RawVG RawVG RawVG RawVG RawVG
RectangleVG RectangleVG RectangleVG RectangleVG RectangleVG
Block graphics
(2D array)

List list List List List<of List list List List List<of Type>>
Data I/O Text file input
TextFileReader TextFileReader TextFileReader TextFileReader TextFileReader
Text file output
TextFileWriter TextFileWriter TextFileWriter TextFileWriter TextFileWriter
User-defined Enumeration
enum NameName
? value1, value2values?values?1 class NameName?(Enum):
value1 = 1
value2 = 2
values?
1
main()
enum NameName? {value1, value2values?values?}1 Enum NameName?
value1 = 0
value2 = 1
End Enum
values?
1
enum NameName? {value1, value2values?values?}1
Concrete class
class Name Code does not parse as Elan. class Name Code does not parse as Elan. class Name Code does not parse as Elan. class Name Code does not parse as Elan. class Name Code does not parse as Elan.
Abstract class
abstract class Name Code does not parse as Elan. abstract class Name Code does not parse as Elan. abstract class Name Code does not parse as Elan. abstract class Name Code does not parse as Elan. abstract class Name Code does not parse as Elan.
Interface
interface Name Code does not parse as Elan. interface Name Code does not parse as Elan. interface Name Code does not parse as Elan. interface Name Code does not parse as Elan. interface Name Code does not parse as Elan.
Other Random number
Random Random Random Random Random
Argument reference
AsRef AsRef AsRef AsRef AsRef
Function reference
Func
Maybe
Maybe Maybe Maybe Maybe Maybe

Named values are statically typed: their Type, once defined, cannot be changed. A named value's Type is:

Mutability of named values

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 valuenamed valuenotes
withas
mutable Type
as
immutable Type
re-assign
with 'set'
mutatescope
constantglobal values are set only at compile time
variable definitionlocal values are set at execution
parameter (by value)local formal input argument
of function, procedure or lambda
parameter (by reference)local formal input and output argument
of function, procedure or lambda

Identifiers and Type names

Named value identifier

The name given to every named value must follow the rules for an identifier:

Type name

Every Type, whether system (e.g. List list List List List) or user-defined (e.g. class MyClass Code does not parse as Elan. class MyClass Code does not parse as Elan. class MyClass Code does not parse as Elan. class MyClass Code does not parse as Elan. class MyClass Code does not parse as Elan.), is named like an identifier but with one important difference: its first character must be an uppercase letter. Type names are also case-sensitive.

Scope and name qualification

'Scope' in the table above refers to where in your code a named value is accessible. Global constants and enums, being the only values having global scope, can be referenced from anywhere in your program. Every other kind of named value is local and restricted to the procedure or function within which it is defined.

Local named values can have the same name as a global constant, function, or procedure (which are all defined at global level or are 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 prefix the name with a dot qualifier of either global. or library. as appropriate

Dot syntax

Reference to a named value is in many cases made with dot syntax, also referred to as applying a dot method. Here are its uses:

You cannot use dot syntax with functions and procedures that you define at the global level, nor with some system functions (e.g. abs) and procedures (e.g. clearPrintedText).

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, concrete class, abstract class, interface, constant and enum.

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 1 variable liname? set to [3, 6, 1, 0, 99, 4, 67]value or expression?2 call inPlaceRippleSortprocedureName?(liarguments?)3 call printprocedureName?(liarguments?)4 end main
+def main() -> None: 1 liname? = [3, 6, 1, 0, 99, 4, 67]value or expression? # variable definition2 inPlaceRippleSortprocedureName?(liarguments?) # call procedure3 printprocedureName?(liarguments?)4 main()
+static void main() { 1 var liname? = [3, 6, 1, 0, 99, 4, 67]value or expression?;2 inPlaceRippleSortprocedureName?(liarguments?); // call procedure3 printprocedureName?(liarguments?);4 }
+Sub main() 1 Dim liname? = {3, 6, 1, 0, 99, 4, 67}value or expression? ' variable definition2 inPlaceRippleSortprocedureName?(liarguments?) ' call procedure3 printprocedureName?(liarguments?)4 End Sub
+static void main() { 1 var liname? = [3, 6, 1, 0, 99, 4, 67]value or expression?;2 inPlaceRippleSortprocedureName?(liarguments?); // call procedure3 printprocedureName?(liarguments?);4 }

procedure

A procedure is a named piece of code that may have parameters that are given inputs via arguments in a call procedure instruction.

Its name must follow the rules for an identifier.

Unlike a function:

The instructions within a procedure can therefore:

Procedure calls are of two kinds:

These calls are to library procedures, but the same options are applicable to the procedures you write.

Example of procedure

   ● Procedure to order a list of integers:

+procedure inPlaceRippleSortname?(li as List<of Int>parameter definitions?) 1 variable hasChangedname? set to truevalue or expression?2 variable lastCompname? set to li.length() - 2value or expression?3 +while hasChanged is truecondition? 4 set hasChangedvariableName? to falsevalue or expression?5 +for iitem? in range(0, lastComp + 1)source? 6 +if li[i] > li[i + 1]condition? then 7 variable tempname? set to li[i]value or expression?8 set li[i]variableName? to li[i + 1]value or expression?9 set li[i + 1]variableName? to tempvalue or expression?10 set hasChangedvariableName? to truevalue or expression?11 end if end for set lastCompvariableName? to lastComp - 1value or expression?12 end while end procedure
+def inPlaceRippleSortname?(li: list[int]parameter definitions?) -> None: # procedure1 hasChangedname? = Truevalue or expression? # variable definition2 lastCompname? = li.length() - 2value or expression? # variable definition3 +while hasChanged == Truecondition?: 4 hasChangedvariableName? = Falsevalue or expression? # re-assign variable5 +for iitem? in range(0, lastComp + 1)source?: 6 +if li[i] > li[i + 1]condition?: 7 tempname? = li[i]value or expression? # variable definition8 li[i]variableName? = li[i + 1]value or expression? # re-assign variable9 li[i + 1]variableName? = tempvalue or expression? # re-assign variable10 hasChangedvariableName? = Truevalue or expression? # re-assign variable11 lastCompvariableName? = lastComp - 1value or expression? # re-assign variable12 main()
+static void inPlaceRippleSortname?(List<int> liparameter definitions?) { // procedure1 var hasChangedname? = truevalue or expression?;2 var lastCompname? = li.length() - 2value or expression?;3 +while (hasChanged == truecondition?) { 4 hasChangedvariableName? = falsevalue or expression?; // re-assign variable5 +foreach (iitem? in range(0, lastComp + 1)source?) { 6 +if (li[i] > li[i + 1]condition?) { 7 var tempname? = li[i]value or expression?;8 li[i]variableName? = li[i + 1]value or expression?; // re-assign variable9 li[i + 1]variableName? = tempvalue or expression?; // re-assign variable10 hasChangedvariableName? = truevalue or expression?; // re-assign variable11 } } lastCompvariableName? = lastComp - 1value or expression?; // re-assign variable12 } }
+Sub inPlaceRippleSortname?(li As List(Of Integer)parameter definitions?) ' procedure1 Dim hasChangedname? = Truevalue or expression? ' variable definition2 Dim lastCompname? = li.length() - 2value or expression? ' variable definition3 +While hasChanged = Truecondition? 4 hasChangedvariableName? = Falsevalue or expression? ' re-assign variable5 +For Each iitem? In range(0, lastComp + 1)source? 6 +If li[i] > li[i + 1]condition? Then 7 Dim tempname? = li[i]value or expression? ' variable definition8 li[i]variableName? = li[i + 1]value or expression? ' re-assign variable9 li[i + 1]variableName? = tempvalue or expression? ' re-assign variable10 hasChangedvariableName? = Truevalue or expression? ' re-assign variable11 End If Next i lastCompvariableName? = lastComp - 1value or expression? ' re-assign variable12 End While End Sub
+static void inPlaceRippleSortname?(List<int> liparameter definitions?) { // procedure1 var hasChangedname? = truevalue or expression?;2 var lastCompname? = li.length() - 2value or expression?;3 +while (hasChanged == truecondition?) { 4 hasChangedvariableName? = falsevalue or expression?; // re-assign variable5 +foreach (iitem? in range(0, lastComp + 1)source?) { 6 +if (li[i] > li[i + 1]condition?) { 7 var tempname? = li[i]value or expression?;8 li[i]variableName? = li[i + 1]value or expression?; // re-assign variable9 li[i + 1]variableName? = tempvalue or expression?; // re-assign variable10 hasChangedvariableName? = truevalue or expression?; // re-assign variable11 } } lastCompvariableName? = lastComp - 1value or expression?; // re-assign variable12 } }

Call by reference

To change the value in named value arg supplied as an argument in a procedure call, both the call argument and the procedure parameter must use a reference (or pointer) to it, defined as Type AsRef AsRef AsRef AsRef AsRef. This then allows use of the dot methods value and set on it to read and change its value, as in this example:

Example of call by reference

   ● argRef is a pointer to variable arg in main, so that arg can be accessed elsewhere by reference:

+main 1 variable argname? set to "abc"value or expression?2 variable argRefname? set to new AsRef<of String>(arg)value or expression?3 call changeArgprocedureName?(argRefarguments?)4 call printprocedureName?(argRef.value()arguments?)5 end main +procedure changeArgname?(pointer as AsRef<of String>parameter definitions?) 6 variable rname? set to pointer.value()value or expression?7 call pointer.setprocedureName?(r.upperCase()arguments?)8 end procedure
+def main() -> None: 1 argname? = "abc"value or expression? # variable definition2 argRefname? = AsRef[str](arg)value or expression? # variable definition3 changeArgprocedureName?(argRefarguments?) # call procedure4 printprocedureName?(argRef.value()arguments?)5 +def changeArgname?(pointer: AsRef[str]parameter definitions?) -> None: # procedure6 rname? = pointer.value()value or expression? # variable definition7 pointer.setprocedureName?(r.upperCase()arguments?) # call procedure8 main()
+static void main() { 1 var argname? = "abc"value or expression?;2 var argRefname? = new AsRef<string>(arg)value or expression?;3 changeArgprocedureName?(argRefarguments?); // call procedure4 printprocedureName?(argRef.value()arguments?);5 } +static void changeArgname?(AsRef<string> pointerparameter definitions?) { // procedure6 var rname? = pointer.value()value or expression?;7 pointer.setprocedureName?(r.upperCase()arguments?); // call procedure8 }
+Sub main() 1 Dim argname? = "abc"value or expression? ' variable definition2 Dim argRefname? = New AsRef(Of String)(arg)value or expression? ' variable definition3 changeArgprocedureName?(argRefarguments?) ' call procedure4 printprocedureName?(argRef.value()arguments?)5 End Sub +Sub changeArgname?(pointer As AsRef(Of String)parameter definitions?) ' procedure6 Dim rname? = pointer.value()value or expression? ' variable definition7 pointer.setprocedureName?(r.upperCase()arguments?) ' call procedure8 End Sub
+static void main() { 1 var argname? = "abc"value or expression?;2 var argRefname? = new AsRef<String>(arg)value or expression?;3 changeArgprocedureName?(argRefarguments?); // call procedure4 printprocedureName?(argRef.value()arguments?);5 } +static void changeArgname?(AsRef<String> pointerparameter definitions?) { // procedure6 var rname? = pointer.value()value or expression?;7 pointer.setprocedureName?(r.upperCase()arguments?); // call procedure8 }

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 an instruction or expression. Its name must follow the rules for an identifier.

Unlike a procedure:

A function definition is comprised of its unique name and, in brackets, its input parameters with their Types, followed by the keyword returns, and the Type of the value that will be returned.

A return instruction is automatically added as the last instruction of a function (since a function must return a value), and there can be only one return instruction in a function. You follow the return instruction with the value to be returned by the function. This may be an expression that will be evaluated before returning. For example:

+function fooname?(a as Int, b as Intparameter definitions?) returns BooleanType? 1 return if(pow(a, b) < pow(b, a), true, false)value or expression?2 end function
+def fooname?(a: int, b: intparameter definitions?) -> boolType?: # function1 return if(pow(a, b) < pow(b, a), True, False)value or expression?2 main()
+static boolType? fooname?(int a, int bparameter definitions?) { // function1 return if(pow(a, b) < pow(b, a), true, false)value or expression?;2 }
+Function fooname?(a As Integer, b As Integerparameter definitions?) As BooleanType? 1 Return if(pow(a, b) < pow(b, a), True, False)value or expression?2 End Function
+static boolType? fooname?(int a, int bparameter definitions?) { // function1 return if(pow(a, b) < pow(b, a), true, false)value or expression?;2 }

Example of a function and reference to it

   ● The function definition:

+function scorename?(g as Gameparameter definitions?) returns IntType? 1 return g.body.length() - 2value or expression?2 end function
+def scorename?(g: Gameparameter definitions?) -> intType?: # function1 return g.body.length() - 2value or expression?2 main()
+static intType? scorename?(Game gparameter definitions?) { // function1 return g.body.length() - 2value or expression?;2 }
+Function scorename?(g As Gameparameter definitions?) As IntegerType? 1 Return g.body.length() - 2value or expression?2 End Function
+static intType? scorename?(Game gparameter definitions?) { // function1 return g.body.length() - 2value or expression?;2 }

   ● Reference to the function:

+main 1 variable gname? set to new Game()value or expression?2 call printprocedureName?(score(g)arguments?)3 end main
+def main() -> None: 1 gname? = Game()value or expression? # variable definition2 printprocedureName?(score(g)arguments?)3 main()
+static void main() { 1 var gname? = new Game()value or expression?;2 printprocedureName?(score(g)arguments?);3 }
+Sub main() 1 Dim gname? = New Game()value or expression? ' variable definition2 printprocedureName?(score(g)arguments?)3 End Sub
+static void main() { 1 var gname? = new Game()value or expression?;2 printprocedureName?(score(g)arguments?);3 }

Recursion

Procedures and functions may be called or referenced recursively, as in this simple factorial calculation:

+function factorialname?(n as Intparameter definitions?) returns IntType? 1 return (if(n > 1, n*factorial(n - 1), 1))value or expression?2 end function
+def factorialname?(n: intparameter definitions?) -> intType?: # function1 return (if(n > 1, n*factorial(n - 1), 1))value or expression?2 main()
+static intType? factorialname?(int nparameter definitions?) { // function1 return (if(n > 1, n*factorial(n - 1), 1))value or expression?;2 }
+Function factorialname?(n As Integerparameter definitions?) As IntegerType? 1 Return (if(n > 1, n*factorial(n - 1), 1))value or expression?2 End Function
+static intType? factorialname?(int nparameter definitions?) { // function1 return (if(n > 1, n*factorial(n - 1), 1))value or expression?;2 }

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 equal instructions show whether your assertions pass or fail.

Example of a set of assert equal instructions that test a function

   ● Test function binarySearch binarySearch binarySearch binarySearch binarySearch on four test lists

+test test_binarySearchtest_name? 1 variable li1name? set to ["lemon", "lime", "orange"]value or expression?2 assert binarySearch(li1, "lemon")actual (computed) value? is trueexpected value? not run3 assert binarySearch(li1, "lime")actual (computed) value? is trueexpected value? not run4 assert binarySearch(li1, "orange")actual (computed) value? is trueexpected value? not run5 assert binarySearch(li1, "pear")actual (computed) value? is falseexpected value? not run6 variable li2name? set to ["lemon", "orange"]value or expression?7 assert binarySearch(li2, "lemon")actual (computed) value? is trueexpected value? not run8 assert binarySearch(li2, "orange")actual (computed) value? is trueexpected value? not run9 assert binarySearch(li2, "pear")actual (computed) value? is falseexpected value? not run10 variable li3name? set to ["lemon"]value or expression?11 assert binarySearch(li3, "lemon")actual (computed) value? is trueexpected value? not run12 assert binarySearch(li3, "lime")actual (computed) value? is falseexpected value? not run13 variable li4name? set to new List<of String>()value or expression?14 assert binarySearch(li4, "pear")actual (computed) value? is falseexpected value? not run15 end test
+def test_binarySearchtest_name?(self) -> None: 1 li1name? = ["lemon", "lime", "orange"]value or expression? # variable definition2 self.assertEqual(binarySearch(li1, "lemon")actual (computed) value?, Trueexpected value?) not run3 self.assertEqual(binarySearch(li1, "lime")actual (computed) value?, Trueexpected value?) not run4 self.assertEqual(binarySearch(li1, "orange")actual (computed) value?, Trueexpected value?) not run5 self.assertEqual(binarySearch(li1, "pear")actual (computed) value?, Falseexpected value?) not run6 li2name? = ["lemon", "orange"]value or expression? # variable definition7 self.assertEqual(binarySearch(li2, "lemon")actual (computed) value?, Trueexpected value?) not run8 self.assertEqual(binarySearch(li2, "orange")actual (computed) value?, Trueexpected value?) not run9 self.assertEqual(binarySearch(li2, "pear")actual (computed) value?, Falseexpected value?) not run10 li3name? = ["lemon"]value or expression? # variable definition11 self.assertEqual(binarySearch(li3, "lemon")actual (computed) value?, Trueexpected value?) not run12 self.assertEqual(binarySearch(li3, "lime")actual (computed) value?, Falseexpected value?) not run13 li4name? = list[str]()value or expression? # variable definition14 self.assertEqual(binarySearch(li4, "pear")actual (computed) value?, Falseexpected value?) not run15 main()
+[TestMethod] static void test_binarySearchtest_name?() { 1 var li1name? = ["lemon", "lime", "orange"]value or expression?;2 Assert.AreEqual(trueexpected value?, binarySearch(li1, "lemon")actual (computed) value?) not run3 Assert.AreEqual(trueexpected value?, binarySearch(li1, "lime")actual (computed) value?) not run4 Assert.AreEqual(trueexpected value?, binarySearch(li1, "orange")actual (computed) value?) not run5 Assert.AreEqual(falseexpected value?, binarySearch(li1, "pear")actual (computed) value?) not run6 var li2name? = ["lemon", "orange"]value or expression?;7 Assert.AreEqual(trueexpected value?, binarySearch(li2, "lemon")actual (computed) value?) not run8 Assert.AreEqual(trueexpected value?, binarySearch(li2, "orange")actual (computed) value?) not run9 Assert.AreEqual(falseexpected value?, binarySearch(li2, "pear")actual (computed) value?) not run10 var li3name? = ["lemon"]value or expression?;11 Assert.AreEqual(trueexpected value?, binarySearch(li3, "lemon")actual (computed) value?) not run12 Assert.AreEqual(falseexpected value?, binarySearch(li3, "lime")actual (computed) value?) not run13 var li4name? = new List<string>()value or expression?;14 Assert.AreEqual(falseexpected value?, binarySearch(li4, "pear")actual (computed) value?) not run15 }
+<TestMethod> Sub test_binarySearchtest_name?() 1 Dim li1name? = {"lemon", "lime", "orange"}value or expression? ' variable definition2 Assert.AreEqual(Trueexpected value?, binarySearch(li1, "lemon")actual (computed) value?) not run3 Assert.AreEqual(Trueexpected value?, binarySearch(li1, "lime")actual (computed) value?) not run4 Assert.AreEqual(Trueexpected value?, binarySearch(li1, "orange")actual (computed) value?) not run5 Assert.AreEqual(Falseexpected value?, binarySearch(li1, "pear")actual (computed) value?) not run6 Dim li2name? = {"lemon", "orange"}value or expression? ' variable definition7 Assert.AreEqual(Trueexpected value?, binarySearch(li2, "lemon")actual (computed) value?) not run8 Assert.AreEqual(Trueexpected value?, binarySearch(li2, "orange")actual (computed) value?) not run9 Assert.AreEqual(Falseexpected value?, binarySearch(li2, "pear")actual (computed) value?) not run10 Dim li3name? = {"lemon"}value or expression? ' variable definition11 Assert.AreEqual(Trueexpected value?, binarySearch(li3, "lemon")actual (computed) value?) not run12 Assert.AreEqual(Falseexpected value?, binarySearch(li3, "lime")actual (computed) value?) not run13 Dim li4name? = New List(Of String)()value or expression? ' variable definition14 Assert.AreEqual(Falseexpected value?, binarySearch(li4, "pear")actual (computed) value?) not run15 End Sub
+@Test static void test_binarySearchtest_name?() { 1 var li1name? = ["lemon", "lime", "orange"]value or expression?;2 assertEquals(trueexpected value?, binarySearch(li1, "lemon")actual (computed) value?) not run3 assertEquals(trueexpected value?, binarySearch(li1, "lime")actual (computed) value?) not run4 assertEquals(trueexpected value?, binarySearch(li1, "orange")actual (computed) value?) not run5 assertEquals(falseexpected value?, binarySearch(li1, "pear")actual (computed) value?) not run6 var li2name? = ["lemon", "orange"]value or expression?;7 assertEquals(trueexpected value?, binarySearch(li2, "lemon")actual (computed) value?) not run8 assertEquals(trueexpected value?, binarySearch(li2, "orange")actual (computed) value?) not run9 assertEquals(falseexpected value?, binarySearch(li2, "pear")actual (computed) value?) not run10 var li3name? = ["lemon"]value or expression?;11 assertEquals(trueexpected value?, binarySearch(li3, "lemon")actual (computed) value?) not run12 assertEquals(falseexpected value?, binarySearch(li3, "lime")actual (computed) value?) not run13 var li4name? = new List<String>()value or expression?;14 assertEquals(falseexpected value?, binarySearch(li4, "pear")actual (computed) value?) not run15 }

Notes

Testing Float values

When testing Float float double Double double 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 test_roundtest_name? 1 assert sqrt(2).round(3)actual (computed) value? is 1.414expected value? not run2 end test
+def test_roundtest_name?(self) -> None: 1 self.assertEqual(sqrt(2).round(3)actual (computed) value?, 1.414expected value?) not run2 main()
+[TestMethod] static void test_roundtest_name?() { 1 Assert.AreEqual(1.414expected value?, sqrt(2).round(3)actual (computed) value?) not run2 }
+<TestMethod> Sub test_roundtest_name?() 1 Assert.AreEqual(1.414expected value?, sqrt(2).round(3)actual (computed) value?) not run2 End Sub
+@Test static void test_roundtest_name?() { 1 assertEquals(1.414expected value?, sqrt(2).round(3)actual (computed) value?) not run2 }

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:

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

+test test_listtest_name? 1 variable aname? set to [5, 1, 7]value or expression?2 assert a[0]actual (computed) value? is 5expected value? not run3 assert a[2]actual (computed) value? is 7expected value? not run4 end test
+def test_listtest_name?(self) -> None: 1 aname? = [5, 1, 7]value or expression? # variable definition2 self.assertEqual(a[0]actual (computed) value?, 5expected value?) not run3 self.assertEqual(a[2]actual (computed) value?, 7expected value?) not run4 main()
+[TestMethod] static void test_listtest_name?() { 1 var aname? = [5, 1, 7]value or expression?;2 Assert.AreEqual(5expected value?, a[0]actual (computed) value?) not run3 Assert.AreEqual(7expected value?, a[2]actual (computed) value?) not run4 }
+<TestMethod> Sub test_listtest_name?() 1 Dim aname? = {5, 1, 7}value or expression? ' variable definition2 Assert.AreEqual(5expected value?, a[0]actual (computed) value?) not run3 Assert.AreEqual(7expected value?, a[2]actual (computed) value?) not run4 End Sub
+@Test static void test_listtest_name?() { 1 var aname? = [5, 1, 7]value or expression?;2 assertEquals(5expected value?, a[0]actual (computed) value?) not run3 assertEquals(7expected value?, a[2]actual (computed) value?) not run4 }

This assert shows how testing can also be done against an expected error message:

+test test_messagetest_name? 1 assert a[4]actual (computed) value? is 0expected value? not run2 assert a[4]actual (computed) value? is "Out of range index: 4 size: 3"expected value? not run3 end test
+def test_messagetest_name?(self) -> None: 1 self.assertEqual(a[4]actual (computed) value?, 0expected value?) not run2 self.assertEqual(a[4]actual (computed) value?, "Out of range index: 4 size: 3"expected value?) not run3 main()
+[TestMethod] static void test_messagetest_name?() { 1 Assert.AreEqual(0expected value?, a[4]actual (computed) value?) not run2 Assert.AreEqual("Out of range index: 4 size: 3"expected value?, a[4]actual (computed) value?) not run3 }
+<TestMethod> Sub test_messagetest_name?() 1 Assert.AreEqual(0expected value?, a[4]actual (computed) value?) not run2 Assert.AreEqual("Out of range index: 4 size: 3"expected value?, a[4]actual (computed) value?) not run3 End Sub
+@Test static void test_messagetest_name?() { 1 assertEquals(0expected value?, a[4]actual (computed) value?) not run2 assertEquals("Out of range index: 4 size: 3"expected value?, a[4]actual (computed) value?) not run3 }

Testing long strings

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.

Example of testing long strings

   ● Reporting the first character at which the actual (computed) and expected strings differ:

+constant sGWname? set to "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"literal value or data structure?1 +function setInStylename?(s as Stringparameter definitions?) returns StringType? 2 return "<style>" + s + "<style>"value or expression?3 end function +test test_setInStyletest_name? 4 assert setInStyle(sGW)actual (computed) value? is "<style>" + sGW + "</style>"expected value? not run5 # assert fail message will be "s found at [146] {expected: /)"comment? end test
+sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"literal value or data structure? # constant1 +def setInStylename?(s: strparameter definitions?) -> strType?: # function2 return "<style>" + s + "<style>"value or expression?3 +def test_setInStyletest_name?(self) -> None: 4 self.assertEqual(setInStyle(sGW)actual (computed) value?, "<style>" + sGW + "</style>"expected value?) not run5 # assert fail message will be "s found at [146] {expected: /)"comment? main()
+const String sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"literal value or data structure?1 +static stringType? setInStylename?(string sparameter definitions?) { // function2 return "<style>" + s + "<style>"value or expression?;3 } +[TestMethod] static void test_setInStyletest_name?() { 4 Assert.AreEqual("<style>" + sGW + "</style>"expected value?, setInStyle(sGW)actual (computed) value?) not run5 // assert fail message will be "s found at [146] {expected: /)"comment? }
+Const sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"literal value or data structure?1 +Function setInStylename?(s As Stringparameter definitions?) As StringType? 2 Return "<style>" + s + "<style>"value or expression?3 End Function +<TestMethod> Sub test_setInStyletest_name?() 4 Assert.AreEqual("<style>" + sGW + "</style>"expected value?, setInStyle(sGW)actual (computed) value?) not run5 ' assert fail message will be "s found at [146] {expected: /)"comment? End Sub
+final String sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"literal value or data structure? // constant1 +static StringType? setInStylename?(String sparameter definitions?) { // function2 return "<style>" + s + "<style>"value or expression?;3 } +@Test static void test_setInStyletest_name?() { 4 assertEquals("<style>" + sGW + "</style>"expected value?, setInStyle(sGW)actual (computed) value?) not run5 // assert fail message will be "s found at [146] {expected: /)"comment? }

Ignoring tests

To ignore tests, use ghosting ghosting ghosting ghosting ghosting.

Even when tests or assert equals are ghosted ghosted ghosted ghosted 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).

Examples of ghosting tests

   ● Ghosting an entire test:

+test test_overAppletest_name? variable g1name? set to new Game(new Random())value or expression? variable g2name? set to g1.withApple(new Square(23, 15))value or expression? assert headOverApple(g2)actual (computed) value? is falseexpected value? not run variable g3name? set to g2.withHead(new Square(23, 15))value or expression? assert headOverApple(g3)actual (computed) value? is trueexpected value? not run end test
+def test_overAppletest_name?(self) -> None: g1name? = Game(Random())value or expression? # variable definition g2name? = g1.withApple(Square(23, 15))value or expression? # variable definition self.assertEqual(headOverApple(g2)actual (computed) value?, Falseexpected value?) not run g3name? = g2.withHead(Square(23, 15))value or expression? # variable definition self.assertEqual(headOverApple(g3)actual (computed) value?, Trueexpected value?) not run main()
+[TestMethod] static void test_overAppletest_name?() { var g1name? = new Game(new Random())value or expression?; var g2name? = g1.withApple(new Square(23, 15))value or expression?; Assert.AreEqual(falseexpected value?, headOverApple(g2)actual (computed) value?) not run var g3name? = g2.withHead(new Square(23, 15))value or expression?; Assert.AreEqual(trueexpected value?, headOverApple(g3)actual (computed) value?) not run }
+<TestMethod> Sub test_overAppletest_name?() Dim g1name? = New Game(New Random())value or expression? ' variable definition Dim g2name? = g1.withApple(New Square(23, 15))value or expression? ' variable definition Assert.AreEqual(Falseexpected value?, headOverApple(g2)actual (computed) value?) not run Dim g3name? = g2.withHead(New Square(23, 15))value or expression? ' variable definition Assert.AreEqual(Trueexpected value?, headOverApple(g3)actual (computed) value?) not run End Sub
+@Test static void test_overAppletest_name?() { var g1name? = new Game(new Random())value or expression?; var g2name? = g1.withApple(new Square(23, 15))value or expression?; assertEquals(falseexpected value?, headOverApple(g2)actual (computed) value?) not run var g3name? = g2.withHead(new Square(23, 15))value or expression?; assertEquals(trueexpected value?, headOverApple(g3)actual (computed) value?) not run }

   ● Ghosting an assert:

+test test_overAppletest_name? 1 variable g1name? set to new Game(new Random())value or expression?2 variable g2name? set to g1.withApple(new Square(23, 15))value or expression?3 assert headOverApple(g2)actual (computed) value? is trueexpected value? not run variable g3name? set to g2.withHead(new Square(23, 15))value or expression?4 assert headOverApple(g3)actual (computed) value? is trueexpected value? not run5 end test
+def test_overAppletest_name?(self) -> None: 1 g1name? = Game(Random())value or expression? # variable definition2 g2name? = g1.withApple(Square(23, 15))value or expression? # variable definition3 self.assertEqual(headOverApple(g2)actual (computed) value?, Trueexpected value?) not run g3name? = g2.withHead(Square(23, 15))value or expression? # variable definition4 self.assertEqual(headOverApple(g3)actual (computed) value?, Trueexpected value?) not run5 main()
+[TestMethod] static void test_overAppletest_name?() { 1 var g1name? = new Game(new Random())value or expression?;2 var g2name? = g1.withApple(new Square(23, 15))value or expression?;3 Assert.AreEqual(trueexpected value?, headOverApple(g2)actual (computed) value?) not run var g3name? = g2.withHead(new Square(23, 15))value or expression?;4 Assert.AreEqual(trueexpected value?, headOverApple(g3)actual (computed) value?) not run5 }
+<TestMethod> Sub test_overAppletest_name?() 1 Dim g1name? = New Game(New Random())value or expression? ' variable definition2 Dim g2name? = g1.withApple(New Square(23, 15))value or expression? ' variable definition3 Assert.AreEqual(Trueexpected value?, headOverApple(g2)actual (computed) value?) not run Dim g3name? = g2.withHead(New Square(23, 15))value or expression? ' variable definition4 Assert.AreEqual(Trueexpected value?, headOverApple(g3)actual (computed) value?) not run5 End Sub
+@Test static void test_overAppletest_name?() { 1 var g1name? = new Game(new Random())value or expression?;2 var g2name? = g1.withApple(new Square(23, 15))value or expression?;3 assertEquals(trueexpected value?, headOverApple(g2)actual (computed) value?) not run var g3name? = g2.withHead(new Square(23, 15))value or expression?;4 assertEquals(trueexpected value?, headOverApple(g3)actual (computed) value?) not run5 }

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 test test test test.

concrete class

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

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

Defining a concrete class

A concrete class is instantiated using the concrete class instruction followed by the class name, and optional inherits to refer to a menu option class or abstract class defined above in the program.

A class definition must include properties, a constructor and at least one method called toString.

Its properties must all be initialised in the constructor.

It may contain functions and procedure methods.

The constructor may optionally define parameters to force the calling code to provide initial values.

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 in the class or outside).

A simple demonstration of a concrete class is:

+main 1 variable xname? set to new Thing()value or expression?2 call printprocedureName?(x.names.toString()arguments?)3 end main +class ThingName? inheritance?4 property namesname? as List<of String>Type?5 +constructor(parameter definitionsparameter definitions?) 6 set this.namesvariableName? to createList(6, "X")value or expression?7 end constructor +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 8 return ""value or expression?9 end function end class
+def main() -> None: 1 xname? = Thing()value or expression? # variable definition2 printprocedureName?(x.names.toString()arguments?)3 +class ThingName? inheritance? # concrete class4 namesname?: list[str]Type? # property5 +def __init__(self: Thing) -> None: 6 self.namesvariableName? = createList(6, "X")value or expression? # re-assign variable7 +def toStringname?(self: Thing) -> strType?: # function method8 return ""value or expression?9 main()
+static void main() { 1 var xname? = new Thing()value or expression?;2 printprocedureName?(x.names.toString()arguments?);3 } +class ThingName? inheritance? {4 public List<string>Type? namesname? {get; private set;} // property5 +public Thing(parameter definitionsparameter definitions?) { 6 this.namesvariableName? = createList(6, "X")value or expression?; // re-assign variable7 } +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method8 return ""value or expression?;9 } }
+Sub main() 1 Dim xname? = New Thing()value or expression? ' variable definition2 printprocedureName?(x.names.toString()arguments?)3 End Sub +Class ThingName? inheritance? inheritance?4 Property namesname? As List(Of String)Type?5 +Sub New(parameter definitionsparameter definitions?) 6 Me.namesvariableName? = createList(6, "X")value or expression? ' re-assign variable7 End Sub +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 8 Return ""value or expression?9 End Function End Class
+static void main() { 1 var xname? = new Thing()value or expression?;2 printprocedureName?(x.names.toString()arguments?);3 } +class ThingName? inheritance? inheritance? {4 public List<String>Type? namesname?; // property5 +public Thing(parameter definitionsparameter definitions?) { 6 this.namesvariableName? = createList(6, "X")value or expression?; // re-assign variable7 } +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method8 return ""value or expression?;9 } }

Here is an example of concrete class definition taken from demo program snake - object-oriented:

Example concrete class definition

   ● Apple:

+class AppleName? inheritance?1 +constructor(parameter definitionsparameter definitions?) 2 set this.locationvariableName? to new Square(0, 0)value or expression?3 end constructor +function toStringname?(parameter definitionsparameter definitions?) returns StringType? 4 return ""value or expression?5 end function property locationname? as SquareType?6 +procedure newRandomPositionname?(snake as Snakeparameter definitions?) 7 variable changePositionname? set to truevalue or expression?8 +while changePositioncondition? 9 variable ranXname? set to randint(0, 39)value or expression?10 variable ranYname? set to randint(0, 29)value or expression?11 set this.locationvariableName? to new Square(ranX, ranY)value or expression?12 +if not snake.bodyCovers(this.location)condition? then 13 set changePositionvariableName? to falsevalue or expression?14 end if end while end procedure +procedure updateBlocksname?(blocks as List<of List<of Int>>parameter definitions?) 15 set blocks[this.location.x][this.location.y]variableName? to redvalue or expression?16 end procedure end class
+class AppleName? inheritance? # concrete class1 +def __init__(self: Apple) -> None: 2 self.locationvariableName? = Square(0, 0)value or expression? # re-assign variable3 +def toStringname?(self: Apple) -> strType?: # function method4 return ""value or expression?5 locationname?: SquareType? # property6 +def newRandomPositionname?(self: Apple, snake: Snakeparameter definitions?) -> None: # procedure method7 changePositionname? = Truevalue or expression? # variable definition8 +while changePositioncondition?: 9 ranXname? = randint(0, 39)value or expression? # variable definition10 ranYname? = randint(0, 29)value or expression? # variable definition11 self.locationvariableName? = Square(ranX, ranY)value or expression? # re-assign variable12 +if not snake.bodyCovers(self.location)condition?: 13 changePositionvariableName? = Falsevalue or expression? # re-assign variable14 +def updateBlocksname?(self: Apple, blocks: list[list[int]]parameter definitions?) -> None: # procedure method15 blocks[self.location.x][self.location.y]variableName? = redvalue or expression? # re-assign variable16 main()
+class AppleName? inheritance? {1 +public Apple(parameter definitionsparameter definitions?) { 2 this.locationvariableName? = new Square(0, 0)value or expression?; // re-assign variable3 } +public stringType? toStringname?(parameter definitionsparameter definitions?) { // function method4 return ""value or expression?;5 } public SquareType? locationname? {get; private set;} // property6 +public void newRandomPositionname?(Snake snakeparameter definitions?) { // procedure method7 var changePositionname? = truevalue or expression?;8 +while (changePositioncondition?) { 9 var ranXname? = randint(0, 39)value or expression?;10 var ranYname? = randint(0, 29)value or expression?;11 this.locationvariableName? = new Square(ranX, ranY)value or expression?; // re-assign variable12 +if (!snake.bodyCovers(this.location)condition?) { 13 changePositionvariableName? = falsevalue or expression?; // re-assign variable14 } } } +public void updateBlocksname?(List<List<int>> blocksparameter definitions?) { // procedure method15 blocks[this.location.x][this.location.y]variableName? = redvalue or expression?; // re-assign variable16 } }
+Class AppleName? inheritance? inheritance?1 +Sub New(parameter definitionsparameter definitions?) 2 Me.locationvariableName? = New Square(0, 0)value or expression? ' re-assign variable3 End Sub +Function toStringname?(parameter definitionsparameter definitions?) As StringType? 4 Return ""value or expression?5 End Function Property locationname? As SquareType?6 +Sub newRandomPositionname?(snake As Snakeparameter definitions?) ' procedure method7 Dim changePositionname? = Truevalue or expression? ' variable definition8 +While changePositioncondition? 9 Dim ranXname? = randint(0, 39)value or expression? ' variable definition10 Dim ranYname? = randint(0, 29)value or expression? ' variable definition11 Me.locationvariableName? = New Square(ranX, ranY)value or expression? ' re-assign variable12 +If Not snake.bodyCovers(Me.location)condition? Then 13 changePositionvariableName? = Falsevalue or expression? ' re-assign variable14 End If End While End Sub +Sub updateBlocksname?(blocks As List(Of List(Of Integer))parameter definitions?) ' procedure method15 blocks[Me.location.x][Me.location.y]variableName? = redvalue or expression? ' re-assign variable16 End Sub End Class
+class AppleName? inheritance? inheritance? {1 +public Apple(parameter definitionsparameter definitions?) { 2 this.locationvariableName? = new Square(0, 0)value or expression?; // re-assign variable3 } +public StringType? toStringname?(parameter definitionsparameter definitions?) { // function method4 return ""value or expression?;5 } public SquareType? locationname?; // property6 +public void newRandomPositionname?(Snake snakeparameter definitions?) { // procedure method7 var changePositionname? = truevalue or expression?;8 +while (changePositioncondition?) { 9 var ranXname? = randint(0, 39)value or expression?;10 var ranYname? = randint(0, 29)value or expression?;11 this.locationvariableName? = new Square(ranX, ranY)value or expression?; // re-assign variable12 +if (!snake.bodyCovers(this.location)condition?) { 13 changePositionvariableName? = falsevalue or expression?; // re-assign variable14 } } } +public void updateBlocksname?(List<List<int>> blocksparameter definitions?) { // procedure method15 blocks[this.location.x][this.location.y]variableName? = redvalue or expression?; // re-assign variable16 } }

this

If in the code of class A Code does not parse as Elan. class A Code does not parse as Elan. class A Code does not parse as Elan. class A Code does not parse as Elan. class A Code does not parse as Elan. you want to invoke a method in class S Code does not parse as Elan. class S Code does not parse as Elan. class S Code does not parse as Elan. class S Code does not parse as Elan. class S Code does not parse as Elan. and pass to it the current instance of A A A A A, you can refer to this instance with the keyword this, as shown by this line in demo program snake - object-oriented:

call apple.newRandomPositionprocedureName?(thisarguments?)0
apple.newRandomPositionprocedureName?(selfarguments?) # call procedure0
apple.newRandomPositionprocedureName?(thisarguments?); // call procedure0
apple.newRandomPositionprocedureName?(Mearguments?) ' call procedure0
apple.newRandomPositionprocedureName?(thisarguments?); // call procedure0

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

inherits

A concrete class (often referred to simply as a class) may optionally inherit from just one abstract class A Code does not parse as Elan. abstract class A Code does not parse as Elan. abstract class A Code does not parse as Elan. abstract class A Code does not parse as Elan. abstract class A Code does not parse as Elan. 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 interface(s) that it inherits from, directly or indirectly.

Notes

abstract class

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

As with a concrete class, any of these members may be made private private private private private, after the corresponding instruction has been added, by selecting that member instruction 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 end end end end instruction. For example:

+function calculateDiscountname?(d as Floatparameter definitions?) returns FloatType? 1 return discountvalue or expression?2 end function
+def calculateDiscountname?(d: floatparameter definitions?) -> floatType?: # function1 return discountvalue or expression?2 main()
+static doubleType? calculateDiscountname?(double dparameter definitions?) { // function1 return discountvalue or expression?;2 }
+Function calculateDiscountname?(d As Doubleparameter definitions?) As DoubleType? 1 Return discountvalue or expression?2 End Function
+static doubleType? calculateDiscountname?(double dparameter definitions?) { // function1 return discountvalue or expression?;2 }

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:

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.

Example using interface

   ● Example from a version of program blackjack of:

enum Act stand, draw interface Player abstract property hand as Hand abstract procedure changeScoreBy(amount as Int) end interface abstract class Automated inherits Player property hand as Hand property score as Int abstract function getAction() returns Action procedure changeScoreBy(amount as Int) set property.score to property.score + amount end procedure end abstract class class Dealer inherits Automated constructor(startingPoints as Int) set property.score to startingPoints end constructor function getAction() returns Action return if((property.hand.total < 17), Act.draw, Act.stand) end function end class class HumanPlayer inherits Player 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 Code does not parse as Elan.
enum Act stand, draw interface Player abstract property hand as Hand abstract procedure changeScoreBy(amount as Int) end interface abstract class Automated inherits Player property hand as Hand property score as Int abstract function getAction() returns Action procedure changeScoreBy(amount as Int) set property.score to property.score + amount end procedure end abstract class class Dealer inherits Automated constructor(startingPoints as Int) set property.score to startingPoints end constructor function getAction() returns Action return if((property.hand.total < 17), Act.draw, Act.stand) end function end class class HumanPlayer inherits Player 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 Code does not parse as Elan.
enum Act stand, draw interface Player abstract property hand as Hand abstract procedure changeScoreBy(amount as Int) end interface abstract class Automated inherits Player property hand as Hand property score as Int abstract function getAction() returns Action procedure changeScoreBy(amount as Int) set property.score to property.score + amount end procedure end abstract class class Dealer inherits Automated constructor(startingPoints as Int) set property.score to startingPoints end constructor function getAction() returns Action return if((property.hand.total < 17), Act.draw, Act.stand) end function end class class HumanPlayer inherits Player 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 Code does not parse as Elan.
enum Act stand, draw interface Player abstract property hand as Hand abstract procedure changeScoreBy(amount as Int) end interface abstract class Automated inherits Player property hand as Hand property score as Int abstract function getAction() returns Action procedure changeScoreBy(amount as Int) set property.score to property.score + amount end procedure end abstract class class Dealer inherits Automated constructor(startingPoints as Int) set property.score to startingPoints end constructor function getAction() returns Action return if((property.hand.total < 17), Act.draw, Act.stand) end function end class class HumanPlayer inherits Player 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 Code does not parse as Elan.
enum Act stand, draw interface Player abstract property hand as Hand abstract procedure changeScoreBy(amount as Int) end interface abstract class Automated inherits Player property hand as Hand property score as Int abstract function getAction() returns Action procedure changeScoreBy(amount as Int) set property.score to property.score + amount end procedure end abstract class class Dealer inherits Automated constructor(startingPoints as Int) set property.score to startingPoints end constructor function getAction() returns Action return if((property.hand.total < 17), Act.draw, Act.stand) end function end class class HumanPlayer inherits Player 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 Code does not parse as Elan.

constant

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

A constant is defined by a literal of a value Type, namely Int int int Integer int, Float float double Double double, Boolean bool bool Boolean bool or String str string String String.

A 'constant' data structure can be defined by using a function which is then referenced to make the structure available in a variable definition. See Constant data structure.

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

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

Examples of literal definitions of the valid Types of constant:

+constant maxHitsname? set to 10literal value or data structure?1 +constant turquoisename? set to 0x00ced1literal value or data structure?2 +constant liveCellname? set to blackliteral value or data structure?3 +constant speedOfLightname? set to 299792.458literal value or data structure?4 +constant gameOvername? set to trueliteral value or data structure?5 +constant euroname? set to 0x20acliteral value or data structure?6 +constant warningMsgname? set to "Limit reached"literal value or data structure?7
+maxHitsname? = 10literal value or data structure? # constant1 +turquoisename? = 0x00ced1literal value or data structure? # constant2 +liveCellname? = blackliteral value or data structure? # constant3 +speedOfLightname? = 299792.458literal value or data structure? # constant4 +gameOvername? = Trueliteral value or data structure? # constant5 +euroname? = 0x20acliteral value or data structure? # constant6 +warningMsgname? = "Limit reached"literal value or data structure? # constant7 main()
+const Int maxHitsname? = 10literal value or data structure?1 +const Int turquoisename? = 0x00ced1literal value or data structure?2 +const Int liveCellname? = blackliteral value or data structure?3 +const Float speedOfLightname? = 299792.458literal value or data structure?4 +const Boolean gameOvername? = trueliteral value or data structure?5 +const Int euroname? = 0x20acliteral value or data structure?6 +const String warningMsgname? = "Limit reached"literal value or data structure?7
+Const maxHitsname? = 10literal value or data structure?1 +Const turquoisename? = &H00ced1literal value or data structure?2 +Const liveCellname? = blackliteral value or data structure?3 +Const speedOfLightname? = 299792.458literal value or data structure?4 +Const gameOvername? = Trueliteral value or data structure?5 +Const euroname? = &H20acliteral value or data structure?6 +Const warningMsgname? = "Limit reached"literal value or data structure?7
+final Int maxHitsname? = 10literal value or data structure? // constant1 +final Int turquoisename? = 0x00ced1literal value or data structure? // constant2 +final Int liveCellname? = blackliteral value or data structure? // constant3 +final Float speedOfLightname? = 299792.458literal value or data structure? // constant4 +final Boolean gameOvername? = trueliteral value or data structure? // constant5 +final Int euroname? = 0x20acliteral value or data structure? // constant6 +final String warningMsgname? = "Limit reached"literal value or data structure? // constant7

Constant data structures

To define a data structure that is to be considered a constant in your program, you have to initialise it with a function whose return value is put into a variable.

These examples define a constant List list List List List and a constant Dictionary Dictionary Dictionary Dictionary Dictionary of colours in indexable variables:

+main 1 variable rainbowname? set to getRainbow()value or expression?2 variable suitColoursname? set to getSuitColours()value or expression?3 end main +function getRainbowname?(parameter definitionsparameter definitions?) returns List<of Int>Type? 4 return [0x9400D3, 0x4B0082, 0x0000CD, 0x008000, 0xFFFF00, 0xFFA500, 0xFF0000]value or expression?5 end function +function getSuitColoursname?(parameter definitionsparameter definitions?) returns Dictionary<of String, Int>Type? 6 return ["spades":black, "hearts":red, "diamonds":red, "clubs":black]value or expression?7 end function
+def main() -> None: 1 rainbowname? = getRainbow()value or expression? # variable definition2 suitColoursname? = getSuitColours()value or expression? # variable definition3 +def getRainbowname?(parameter definitionsparameter definitions?) -> list[int]Type?: # function4 return [0x9400D3, 0x4B0082, 0x0000CD, 0x008000, 0xFFFF00, 0xFFA500, 0xFF0000]value or expression?5 +def getSuitColoursname?(parameter definitionsparameter definitions?) -> Dictionary[str, int]Type?: # function6 return ["spades":black, "hearts":red, "diamonds":red, "clubs":black]value or expression?7 main()
+static void main() { 1 var rainbowname? = getRainbow()value or expression?;2 var suitColoursname? = getSuitColours()value or expression?;3 } +static List<int>Type? getRainbowname?(parameter definitionsparameter definitions?) { // function4 return [0x9400D3, 0x4B0082, 0x0000CD, 0x008000, 0xFFFF00, 0xFFA500, 0xFF0000]value or expression?;5 } +static Dictionary<string, int>Type? getSuitColoursname?(parameter definitionsparameter definitions?) { // function6 return ["spades":black, "hearts":red, "diamonds":red, "clubs":black]value or expression?;7 }
+Sub main() 1 Dim rainbowname? = getRainbow()value or expression? ' variable definition2 Dim suitColoursname? = getSuitColours()value or expression? ' variable definition3 End Sub +Function getRainbowname?(parameter definitionsparameter definitions?) As List(Of Integer)Type? 4 Return {&H9400D3, &H4B0082, &H0000CD, &H008000, &HFFFF00, &HFFA500, &HFF0000}value or expression?5 End Function +Function getSuitColoursname?(parameter definitionsparameter definitions?) As Dictionary(Of String, Integer)Type? 6 Return ["spades":black, "hearts":red, "diamonds":red, "clubs":black]value or expression?7 End Function
+static void main() { 1 var rainbowname? = getRainbow()value or expression?;2 var suitColoursname? = getSuitColours()value or expression?;3 } +static List<int>Type? getRainbowname?(parameter definitionsparameter definitions?) { // function4 return [0x9400D3, 0x4B0082, 0x0000CD, 0x008000, 0xFFFF00, 0xFFA500, 0xFF0000]value or expression?;5 } +static Dictionary<String, int>Type? getSuitColoursname?(parameter definitionsparameter definitions?) { // function6 return ["spades":black, "hearts":red, "diamonds":red, "clubs":black]value or expression?;7 }

enum

An enum – short for 'enumeration' – provides for the simplest form of user-defined Type. It is used to provide a fixed set of options that are self-explanatory, and the references to its those options (its 'values') are checked at compile time.

You define an enum with a Type name (so starting with an uppercase letter) followed by a number of 'values' (separated by commas) which must all 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 made by using dot syntax with method enumValue.

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

Examples of enum definitions:

enum SuitName? spades, hearts, diamonds, clubsvalues?values?1 enum OutcomeName? undecided, win, lose, draw, winDoublevalues?values?2
class SuitName?(Enum):
spades = 1
hearts = 2
diamonds = 3
clubs = 4
values?
1
class OutcomeName?(Enum):
undecided = 1
win = 2
lose = 3
draw = 4
winDouble = 5
values?
2
main()
enum SuitName? {spades, hearts, diamonds, clubsvalues?values?}1 enum OutcomeName? {undecided, win, lose, draw, winDoublevalues?values?}2
Enum SuitName?
spades = 0
hearts = 1
diamonds = 2
clubs = 3
End Enum
values?
1
Enum OutcomeName?
undecided = 0
win = 1
lose = 2
draw = 3
winDouble = 4
End Enum
values?
2
enum SuitName? {spades, hearts, diamonds, clubsvalues?values?}1 enum OutcomeName? {undecided, win, lose, draw, winDoublevalues?values?}2

Examples of use

   ▶ To retrieve a valid 'value':

enum StatusName? pending, playing, standing, blackjack, bustvalues?values?1 +main 2 variable statename? set to Status.pendingvalue or expression?3 end main
class StatusName?(Enum):
pending = 1
playing = 2
standing = 3
blackjack = 4
bust = 5
values?
1
+def main() -> None: 2 statename? = Status.pendingvalue or expression? # variable definition3 main()
enum StatusName? {pending, playing, standing, blackjack, bustvalues?values?}1 +static void main() { 2 var statename? = Status.pendingvalue or expression?;3 }
Enum StatusName?
pending = 0
playing = 1
standing = 2
blackjack = 3
bust = 4
End Enum
values?
1
+Sub main() 2 Dim statename? = Status.pendingvalue or expression? ' variable definition3 End Sub
enum StatusName? {pending, playing, standing, blackjack, bustvalues?values?}1 +static void main() { 2 var statename? = Status.pendingvalue or expression?;3 }

   ▶ To get a valid option from an abbreviation (in demo program pathfinder.elan):

enum AlgorithmName? dijkstra, aStar, heuristicvalues?values?1 +function getAlgFromLettername?(letter as Stringparameter definitions?) returns AlgorithmType? 2 variable algsname? set to ["a":Algorithm.aStar, "d":Algorithm.dijkstra, "h":Algorithm.heuristic]value or expression?3 return algs[letter]value or expression?4 end function
class AlgorithmName?(Enum):
dijkstra = 1
aStar = 2
heuristic = 3
values?
1
+def getAlgFromLettername?(letter: strparameter definitions?) -> AlgorithmType?: # function2 algsname? = ["a":Algorithm.aStar, "d":Algorithm.dijkstra, "h":Algorithm.heuristic]value or expression? # variable definition3 return algs[letter]value or expression?4 main()
enum AlgorithmName? {dijkstra, aStar, heuristicvalues?values?}1 +static AlgorithmType? getAlgFromLettername?(string letterparameter definitions?) { // function2 var algsname? = ["a":Algorithm.aStar, "d":Algorithm.dijkstra, "h":Algorithm.heuristic]value or expression?;3 return algs[letter]value or expression?;4 }
Enum AlgorithmName?
dijkstra = 0
aStar = 1
heuristic = 2
End Enum
values?
1
+Function getAlgFromLettername?(letter As Stringparameter definitions?) As AlgorithmType? 2 Dim algsname? = ["a":Algorithm.aStar, "d":Algorithm.dijkstra, "h":Algorithm.heuristic]value or expression? ' variable definition3 Return algs[letter]value or expression?4 End Function
enum AlgorithmName? {dijkstra, aStar, heuristicvalues?values?}1 +static AlgorithmType? getAlgFromLettername?(String letterparameter definitions?) { // function2 var algsname? = ["a":Algorithm.aStar, "d":Algorithm.dijkstra, "h":Algorithm.heuristic]value or expression?;3 return algs[letter]value or expression?;4 }

   ▶ A function to turn directions held in an enum through 90° clockwise:

enum Compass north, east, south, west main variable dir set to Compass.south call print(enumValue(clockwise(dir)) end main function clockwise(d as Compass) returns Compass variable newDir set to Compass.north if d.equals(Compass.north) then set newDir to Compass.east elif d.equals(Compass.east) then set newDir to Compass.south elif d.equals(Compass.south) then set newDir to Compass.west end if return newDir end function Code does not parse as Elan.
enum Compass north, east, south, west main variable dir set to Compass.south call print(enumValue(clockwise(dir)) end main function clockwise(d as Compass) returns Compass variable newDir set to Compass.north if d.equals(Compass.north) then set newDir to Compass.east elif d.equals(Compass.east) then set newDir to Compass.south elif d.equals(Compass.south) then set newDir to Compass.west end if return newDir end function Code does not parse as Elan.
enum Compass north, east, south, west main variable dir set to Compass.south call print(enumValue(clockwise(dir)) end main function clockwise(d as Compass) returns Compass variable newDir set to Compass.north if d.equals(Compass.north) then set newDir to Compass.east elif d.equals(Compass.east) then set newDir to Compass.south elif d.equals(Compass.south) then set newDir to Compass.west end if return newDir end function Code does not parse as Elan.
enum Compass north, east, south, west main variable dir set to Compass.south call print(enumValue(clockwise(dir)) end main function clockwise(d as Compass) returns Compass variable newDir set to Compass.north if d.equals(Compass.north) then set newDir to Compass.east elif d.equals(Compass.east) then set newDir to Compass.south elif d.equals(Compass.south) then set newDir to Compass.west end if return newDir end function Code does not parse as Elan.
enum Compass north, east, south, west main variable dir set to Compass.south call print(enumValue(clockwise(dir)) end main function clockwise(d as Compass) returns Compass variable newDir set to Compass.north if d.equals(Compass.north) then set newDir to Compass.east elif d.equals(Compass.east) then set newDir to Compass.south elif d.equals(Compass.south) then set newDir to Compass.west end if return newDir end function Code does not parse as Elan.

Member instructions

Member instructions (also referred to simply as 'members') are located within an interface, abstract class or concrete class. The new code prompt offers 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, procedure and constructor are applicable to the various kinds of class.

Abstract Concrete notes
interface abstract class concrete class
abstract property
abstract function prototype for function reference only
abstract procedure prototype for procedure call only
property
function method method toString must be defined
procedure method
private property
private function
private procedure
constructor

constructor

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

If a concrete class's constructornew), the values of the correct Type must be provided. For example, if the class Square Square Square Square Square has this constructor:

class Square constructor(x as Int, y as Int) set property.x to x set property.y to y end constructor end class Code does not parse as Elan.
class Square constructor(x as Int, y as Int) set property.x to x set property.y to y end constructor end class Code does not parse as Elan.
class Square constructor(x as Int, y as Int) set property.x to x set property.y to y end constructor end class Code does not parse as Elan.
class Square constructor(x as Int, y as Int) set property.x to x set property.y to y end constructor end class Code does not parse as Elan.
class Square constructor(x as Int, y as Int) set property.x to x set property.y to y end constructor end class Code does not parse as Elan.

then it may be instantiated like this:
variable tailname? set to new Square(20, 15)value or expression?0 tailname? = Square(20, 15)value or expression? # variable definition0 var tailname? = new Square(20, 15)value or expression?;0 Dim tailname? = New Square(20, 15)value or expression? ' variable definition0 var tailname? = new Square(20, 15)value or expression?;0

property

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

It may be read, but not written to: properties may be modified only from outside the class by means of a procedure method.

It may be given an initial value within a constructor, for example:

class Game property height as Int property board as Board property head as Square property body as List constructor() end constuctor function toString() returns String return "undefined" end function end class Code does not parse as Elan.
class Game property height as Int property board as Board property head as Square property body as List constructor() end constuctor function toString() returns String return "undefined" end function end class Code does not parse as Elan.
class Game property height as Int property board as Board property head as Square property body as List constructor() end constuctor function toString() returns String return "undefined" end function end class Code does not parse as Elan.
class Game property height as Int property board as Board property head as Square property body as List constructor() end constuctor function toString() returns String return "undefined" end function end class Code does not parse as Elan.
class Game property height as Int property board as Board property head as Square property body as List constructor() end constuctor function toString() returns String return "undefined" end function end class Code does not parse as Elan.

function method

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

procedure method

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

abstract property

An abstract property property property property 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.

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 instruction or selecting it and keying Ctrl+p. This action is a toggle used both to set and to remove the modifier private.

class Game constructor(board as Board) set property.board to board end constructor procedure setHeight(height as Int) set property.height to height end procedure end class Code does not parse as Elan.
class Game constructor(board as Board) set property.board to board end constructor procedure setHeight(height as Int) set property.height to height end procedure end class Code does not parse as Elan.
class Game constructor(board as Board) set property.board to board end constructor procedure setHeight(height as Int) set property.height to height end procedure end class Code does not parse as Elan.
class Game constructor(board as Board) set property.board to board end constructor procedure setHeight(height as Int) set property.height to height end procedure end class Code does not parse as Elan.
class Game constructor(board as Board) set property.board to board end constructor procedure setHeight(height as Int) set property.height to height end procedure end class Code does not parse as Elan.

Statement instructions

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

variable definition, re-assign variable, if, while loop, for loop, call procedure, try, throw exception and, within a test, assert equal.

variable definition

The variable definition .. set to .. instruction declares and initialises a mutable named valued. The name defined must be a valid identifier.>/p>

The initial value is given by a following expression. For example: variable notename? set to 440value or expression?0 notename? = 440value or expression? # variable definition0 var notename? = 440value or expression?;0 Dim notename? = 440value or expression? ' variable definition0 var notename? = 440value or expression?;0

re-assign variable

The set variable .. to .. instruction assigns a new value to an existing variable definition (mutable) named value.

The new value must be of the same Type as (or of a Type compatible with) that of the variable.

A re-assign variable instruction may not assign a new value to a parameter within a procedure.

To change the value of an argument supplied in a procedure call, the corresponding parameter must have been defined as of Type AsRef AsRef AsRef AsRef AsRef, which then allows use of the dot method set on it. See Call by reference

if (instruction)

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

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

Examples using if instruction

   ● Simple choice between equality and inequality:

+if head is applecondition? then 0 call setAppleToRandomPositionprocedureName?(apple, bodyarguments?)1 else2 call body.removeAtprocedureName?(0arguments?)3 end if
+if head == applecondition?: 0 setAppleToRandomPositionprocedureName?(apple, bodyarguments?) # call procedure1 else:2 body.removeAtprocedureName?(0arguments?) # call procedure3
+if (head == applecondition?) { 0 setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure1 } else {2 body.removeAtprocedureName?(0arguments?); // call procedure3 }
+If head = applecondition? Then 0 setAppleToRandomPositionprocedureName?(apple, bodyarguments?) ' call procedure1 Else2 body.removeAtprocedureName?(0arguments?) ' call procedure3 End If
+if (head == applecondition?) { 0 setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure1 } else {2 body.removeAtprocedureName?(0arguments?); // call procedure3 }

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

variable resultname? set to falsevalue or expression?0
resultname? = Falsevalue or expression? # variable definition0
var resultname? = falsevalue or expression?;0
Dim resultname? = Falsevalue or expression? ' variable definition0
var resultname? = falsevalue or expression?;0

while loop

The while loop construct provides a conditional loop, used when you want execution of the enclosed instructions to begin only if some condition is true.

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

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

+main 1 variable filename? set to openFileForReading()value or expression?2 +while not file.endOfFile()condition? 3 call printprocedureName?(file.readLine()arguments?)4 end while call file.closeprocedureName?(arguments?)5 end main
+def main() -> None: 1 filename? = openFileForReading()value or expression? # variable definition2 +while not file.endOfFile()condition?: 3 printprocedureName?(file.readLine()arguments?)4 file.closeprocedureName?(arguments?) # call procedure5 main()
+static void main() { 1 var filename? = openFileForReading()value or expression?;2 +while (!file.endOfFile()condition?) { 3 printprocedureName?(file.readLine()arguments?);4 } file.closeprocedureName?(arguments?); // call procedure5 }
+Sub main() 1 Dim filename? = openFileForReading()value or expression? ' variable definition2 +While Not file.endOfFile()condition? 3 printprocedureName?(file.readLine()arguments?)4 End While file.closeprocedureName?(arguments?) ' call procedure5 End Sub
+static void main() { 1 var filename? = openFileForReading()value or expression?;2 +while (!file.endOfFile()condition?) { 3 printprocedureName?(file.readLine()arguments?);4 } file.closeprocedureName?(arguments?); // call procedure5 }

for

The construct

+for pitem? in qsource? 0 new code end for
+for pitem? in qsource?: 0 new code
+foreach (pitem? in qsource?) { 0 new code }
+For Each pitem? In qsource? 0 new code Next p
+foreach (pitem? in qsource?) { 0 new code }
specifies looping through a sequence of characters in a String str string String String or items in a List list List List List. The forms of the for loop are:

Alternatively, the selection can be constrained using methods range and rangeInSteps to specify integer values of the loop variable:

The loop variable p does not have to have been previously declared or initialised.

Examples using for

   ● Function to reverse a String str string String String:

+function reversename?(s as Stringparameter definitions?) returns StringType? 1 variable sReturnname? set to ""value or expression?2 +for chitem? in ssource? 3 set sReturnvariableName? to ch + sReturnvalue or expression?4 end for return sReturnvalue or expression?5 end function
+def reversename?(s: strparameter definitions?) -> strType?: # function1 sReturnname? = ""value or expression? # variable definition2 +for chitem? in ssource?: 3 sReturnvariableName? = ch + sReturnvalue or expression? # re-assign variable4 return sReturnvalue or expression?5 main()
+static stringType? reversename?(string sparameter definitions?) { // function1 var sReturnname? = ""value or expression?;2 +foreach (chitem? in ssource?) { 3 sReturnvariableName? = ch + sReturnvalue or expression?; // re-assign variable4 } return sReturnvalue or expression?;5 }
+Function reversename?(s As Stringparameter definitions?) As StringType? 1 Dim sReturnname? = ""value or expression? ' variable definition2 +For Each chitem? In ssource? 3 sReturnvariableName? = ch + sReturnvalue or expression? ' re-assign variable4 Next ch Return sReturnvalue or expression?5 End Function
+static StringType? reversename?(String sparameter definitions?) { // function1 var sReturnname? = ""value or expression?;2 +foreach (chitem? in ssource?) { 3 sReturnvariableName? = ch + sReturnvalue or expression?; // re-assign variable4 } return sReturnvalue or expression?;5 }

   ● To print the first item in a List list List List List and then each item in it:

+main 1 variable namesname? set to ["Tom", "Dick", "Harriet"]value or expression?2 call printprocedureName?(names[0]arguments?)3 +for nitem? in namessource? 4 call printprocedureName?(narguments?)5 end for end main
+def main() -> None: 1 namesname? = ["Tom", "Dick", "Harriet"]value or expression? # variable definition2 printprocedureName?(names[0]arguments?)3 +for nitem? in namessource?: 4 printprocedureName?(narguments?)5 main()
+static void main() { 1 var namesname? = ["Tom", "Dick", "Harriet"]value or expression?;2 printprocedureName?(names[0]arguments?);3 +foreach (nitem? in namessource?) { 4 printprocedureName?(narguments?);5 } }
+Sub main() 1 Dim namesname? = {"Tom", "Dick", "Harriet"}value or expression? ' variable definition2 printprocedureName?(names[0]arguments?)3 +For Each nitem? In namessource? 4 printprocedureName?(narguments?)5 Next n End Sub
+static void main() { 1 var namesname? = ["Tom", "Dick", "Harriet"]value or expression?;2 printprocedureName?(names[0]arguments?);3 +foreach (nitem? in namessource?) { 4 printprocedureName?(narguments?);5 } }

   ● To print first the keys and then the values from a Dictionary Dictionary Dictionary Dictionary Dictionary:

+main 1 variable diname? set to ["A":1, "B":2, "C":3]value or expression?2 +for sitem? in di.keys()source? 3 call printprocedureName?(sarguments?)4 end for +for nitem? in di.values()source? 5 call printprocedureName?(narguments?)6 end for end main
+def main() -> None: 1 diname? = ["A":1, "B":2, "C":3]value or expression? # variable definition2 +for sitem? in di.keys()source?: 3 printprocedureName?(sarguments?)4 +for nitem? in di.values()source?: 5 printprocedureName?(narguments?)6 main()
+static void main() { 1 var diname? = ["A":1, "B":2, "C":3]value or expression?;2 +foreach (sitem? in di.keys()source?) { 3 printprocedureName?(sarguments?);4 } +foreach (nitem? in di.values()source?) { 5 printprocedureName?(narguments?);6 } }
+Sub main() 1 Dim diname? = ["A":1, "B":2, "C":3]value or expression? ' variable definition2 +For Each sitem? In di.keys()source? 3 printprocedureName?(sarguments?)4 Next s +For Each nitem? In di.values()source? 5 printprocedureName?(narguments?)6 Next n End Sub
+static void main() { 1 var diname? = ["A":1, "B":2, "C":3]value or expression?;2 +foreach (sitem? in di.keys()source?) { 3 printprocedureName?(sarguments?);4 } +foreach (nitem? in di.values()source?) { 5 printprocedureName?(narguments?);6 } }

Technical note about the for loop variable

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

It is therefore recommended never to re-assign the loop variable within the loop.


call procedure

The call procedure instruction is used when you want to run a procedure.

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.

An argument whose value is changed by the procedure must be:

For procedures that you define yourself, any argument whose value is changed must be of a mutable Type.

An expression of the correct Type can be used as an argument, but its value cannot be changed in the procedure.

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. To enforce this, call procedure instructions are not allowed in functions.

There is a limit to the complexity of a call procedure instruction. 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 change variable instruction on the line above. See the error message explanation for 'procedureName' in a call instruction.

try

You can enclose code in a try instruction when you want it to be able, at execution, to throw an exception that you handle.

This might arise when calling a System method that is dependent upon external conditions, such as when cancelling the writing of a text file, as in the example in Writing text files.

You can also use a test to check whether another piece of code might throw an exception by wrapping it in a try instruction.

Examples using try..catch

   ● Use a test to catch an error in a function:

+test test_try_1test_name? 1 +try 2 variable xname? set to foo(0)value or expression?3 call printprocedureName?("not caught"arguments?)4 catch ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError?5 call printprocedureName?("runtime error"arguments?)6 end try end test
+def test_try_1test_name?(self) -> None: 1 +try: 2 xname? = foo(0)value or expression? # variable definition3 printprocedureName?("not caught"arguments?)4 except ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError?: # catch5 printprocedureName?("runtime error"arguments?)6 main()
+[TestMethod] static void test_try_1test_name?() { 1 +try { 2 var xname? = foo(0)value or expression?;3 printprocedureName?("not caught"arguments?);4 } catch (ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? evariableName?) {5 printprocedureName?("runtime error"arguments?);6 } }
+<TestMethod> Sub test_try_1test_name?() 1 +Try 2 Dim xname? = foo(0)value or expression? ' variable definition3 printprocedureName?("not caught"arguments?)4 Catch evariableName? As ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError?5 printprocedureName?("runtime error"arguments?)6 End Try End Sub
+@Test static void test_try_1test_name?() { 1 +try { 2 var xname? = foo(0)value or expression?;3 printprocedureName?("not caught"arguments?);4 } catch (ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? evariableName?) {5 printprocedureName?("runtime error"arguments?);6 } }

   ● To check a function input, and throw an exception up:

+main 1 +try 2 variable tname? set to foo(true)value or expression?3 call printprocedureName?("no error"arguments?)4 catch CustomErrortype e.g. ElanRuntimeError or CustomError?5 call printprocedureName?("error"arguments?)6 end try end main +function fooname?(f as Booleanparameter definitions?) returns BooleanType? 7 +if fcondition? then 8 throw CustomErrorexception type? "error in foo()"message?9 end if return fvalue or expression?10 end function
+def main() -> None: 1 +try: 2 tname? = foo(True)value or expression? # variable definition3 printprocedureName?("no error"arguments?)4 except CustomErrortype e.g. ElanRuntimeError or CustomError?: # catch5 printprocedureName?("error"arguments?)6 +def fooname?(f: boolparameter definitions?) -> boolType?: # function7 +if fcondition?: 8 raise CustomErrorexception type?(""error in foo()"message?")9 return fvalue or expression?10 main()
+static void main() { 1 +try { 2 var tname? = foo(true)value or expression?;3 printprocedureName?("no error"arguments?);4 } catch (CustomErrortype e.g. ElanRuntimeError or CustomError? evariableName?) {5 printprocedureName?("error"arguments?);6 } } +static boolType? fooname?(bool fparameter definitions?) { // function7 +if (fcondition?) { 8 throw new CustomErrorexception type?("error in foo()"message?)9 } return fvalue or expression?;10 }
+Sub main() 1 +Try 2 Dim tname? = foo(True)value or expression? ' variable definition3 printprocedureName?("no error"arguments?)4 Catch evariableName? As CustomErrortype e.g. ElanRuntimeError or CustomError?5 printprocedureName?("error"arguments?)6 End Try End Sub +Function fooname?(f As Booleanparameter definitions?) As BooleanType? 7 +If fcondition? Then 8 Throw New CustomErrorexception type?("error in foo()"message?)9 End If Return fvalue or expression?10 End Function
+static void main() { 1 +try { 2 var tname? = foo(true)value or expression?;3 printprocedureName?("no error"arguments?);4 } catch (CustomErrortype e.g. ElanRuntimeError or CustomError? evariableName?) {5 printprocedureName?("error"arguments?);6 } } +static boolType? fooname?(bool fparameter definitions?) { // function7 +if (fcondition?) { 8 throw new CustomErrorexception type?("error in foo()"message?)9 } return fvalue or expression?;10 }

   ● To stop the program if one of two points (each a Tuple) has negative x or y:

+main 1 variable aname? set to (1.0, 2.0)value or expression?2 variable bname? set to (3.0, -6.0)value or expression?3 variable fQname? set to firstQuadrant(a, b)value or expression?4 end main +function firstQuadrantname?(a as (Float, Float), b as (Float, Float)parameter definitions?) returns FloatType? 5 +if (a.item_0 < 0) or (a.item_1 < 0) or (b.item_0 < 0) or (b.item_1 < 0)condition? then 6 throw CustomErrorexception type? $"point ({a}) or ({b}) is not in the first quadrant"message?7 end if return 0value or expression?8 end function
+def main() -> None: 1 aname? = (1.0, 2.0)value or expression? # variable definition2 bname? = (3.0, -6.0)value or expression? # variable definition3 fQname? = firstQuadrant(a, b)value or expression? # variable definition4 +def firstQuadrantname?(a: tuple[float, float], b: tuple[float, float]parameter definitions?) -> floatType?: # function5 +if (a.item_0 < 0) or (a.item_1 < 0) or (b.item_0 < 0) or (b.item_1 < 0)condition?: 6 raise CustomErrorexception type?("f"point ({a}) or ({b}) is not in the first quadrant"message?")7 return 0value or expression?8 main()
+static void main() { 1 var aname? = (1.0, 2.0)value or expression?;2 var bname? = (3.0, -6.0)value or expression?;3 var fQname? = firstQuadrant(a, b)value or expression?;4 } +static doubleType? firstQuadrantname?((double, double) a, (double, double) bparameter definitions?) { // function5 +if ((a.item_0 < 0) || (a.item_1 < 0) || (b.item_0 < 0) || (b.item_1 < 0)condition?) { 6 throw new CustomErrorexception type?($"point ({a}) or ({b}) is not in the first quadrant"message?)7 } return 0value or expression?;8 }
+Sub main() 1 Dim aname? = (1.0, 2.0)value or expression? ' variable definition2 Dim bname? = (3.0, -6.0)value or expression? ' variable definition3 Dim fQname? = firstQuadrant(a, b)value or expression? ' variable definition4 End Sub +Function firstQuadrantname?(a As (Double, Double), b As (Double, Double)parameter definitions?) As DoubleType? 5 +If (a.item_0 < 0) Or (a.item_1 < 0) Or (b.item_0 < 0) Or (b.item_1 < 0)condition? Then 6 Throw New CustomErrorexception type?($"point ({a}) or ({b}) is not in the first quadrant"message?)7 End If Return 0value or expression?8 End Function
+static void main() { 1 var aname? = (1.0, 2.0)value or expression?;2 var bname? = (3.0, -6.0)value or expression?;3 var fQname? = firstQuadrant(a, b)value or expression?;4 } +static doubleType? firstQuadrantname?((double, double) a, (double, double) bparameter definitions?) { // function5 +if ((a.item_0 < 0) || (a.item_1 < 0) || (b.item_0 < 0) || (b.item_1 < 0)condition?) { 6 throw new CustomErrorexception type?(String.format("point (%) or (%) is not in the first quadrant", a, b)message?)7 } return 0value or expression?;8 }

throw exception

You can deliberately generate, or 'throw', an error exception when a specific circumstance is identified, using a throw exception instruction which defines an explanatory string.

For the program to retain control when an exception is raised, use the try instruction.

Example using throw exception

   ● To stop the program if a value calculated in a function is out of range:

+main 1 call printprocedureName?($"{percent(23, 20)}" + "%"arguments?)2 end main +function percentname?(value as Float, maximum as Floatparameter definitions?) returns FloatType? 3 variable rname? set to 100.0value or expression?4 +if value > maximumcondition? then 5 throw ElanRuntimeErrorexception type? "proportion exceeds 100%"message?6 end if return value/maximum*rvalue or expression?7 end function
+def main() -> None: 1 printprocedureName?(f"{percent(23, 20)}" + "%"arguments?)2 +def percentname?(value: float, maximum: floatparameter definitions?) -> floatType?: # function3 rname? = 100.0value or expression? # variable definition4 +if value > maximumcondition?: 5 raise ElanRuntimeErrorexception type?(""proportion exceeds 100%"message?")6 return value/maximum*rvalue or expression?7 main()
+static void main() { 1 printprocedureName?($"{percent(23, 20)}" + "%"arguments?);2 } +static doubleType? percentname?(double value, double maximumparameter definitions?) { // function3 var rname? = 100.0value or expression?;4 +if (value > maximumcondition?) { 5 throw new ElanRuntimeErrorexception type?("proportion exceeds 100%"message?)6 } return value/maximum*rvalue or expression?;7 }
+Sub main() 1 printprocedureName?($"{percent(23, 20)}" + "%"arguments?)2 End Sub +Function percentname?(value As Double, maximum As Doubleparameter definitions?) As DoubleType? 3 Dim rname? = 100.0value or expression? ' variable definition4 +If value > maximumcondition? Then 5 Throw New ElanRuntimeErrorexception type?("proportion exceeds 100%"message?)6 End If Return value/maximum*rvalue or expression?7 End Function
+static void main() { 1 printprocedureName?(String.formatarguments?);2 } +static doubleType? percentname?(double value, double maximumparameter definitions?) { // function3 var rname? = 100.0value or expression?;4 +if (value > maximumcondition?) { 5 throw new ElanRuntimeErrorexception type?("proportion exceeds 100%"message?)6 } return value/maximum*rvalue or expression?;7 }

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

proportion exceeds 100% Code does not parse as Elan. proportion exceeds 100% Code does not parse as Elan. proportion exceeds 100% Code does not parse as Elan. proportion exceeds 100% Code does not parse as Elan. proportion exceeds 100% Code does not parse as Elan.
Error Code does not parse as Elan. Error Code does not parse as Elan. Error Code does not parse as Elan. Error Code does not parse as Elan. Error Code does not parse as Elan.

assert equal

The assert equal instruction 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 the throw exception instruction.

let statement

Of particular importance in Functional Programming is providing for constant values within a function.

The let statement instruction declares and initialises an immutable named value whose scope is confined to the function.

The name defined must be a valid identifier.

It is recommended that you always use a let statement in preference to a variable definition, unless you need to be able to re-assign its value.

You can declare a such a constant within a loop, so that the named value gets a new value each time it is executed, but its value cannot be changed in any other way.

Unlike a global constant, the value assigned in a let statement may be any value, literal or expression:

+function numbersname?(parameter definitionsparameter definitions?) returns (Float, Float)Type? 1 let tauname? be 2*pivalue or expression?2 let phiname? be (1 + sqrt(5))/2value or expression?3 return (tau, phi)value or expression?4 end function
+def numbersname?(parameter definitionsparameter definitions?) -> tuple[float, float]Type?: # function1 tauname? = 2*pivalue or expression? # let2 phiname? = (1 + sqrt(5))/2value or expression? # let3 return (tau, phi)value or expression?4 main()
+static (double, double)Type? numbersname?(parameter definitionsparameter definitions?) { // function1 var Float tauname? = 2*pivalue or expression?; // let2 var Float phiname? = (1 + sqrt(5))/2value or expression?; // let3 return (tau, phi)value or expression?;4 }
+Function numbersname?(parameter definitionsparameter definitions?) As (Double, Double)Type? 1 Dim tauname? = 2*pivalue or expression? ' let2 Dim phiname? = (1 + sqrt(5))/2value or expression? ' let3 Return (tau, phi)value or expression?4 End Function
+static (double, double)Type? numbersname?(parameter definitionsparameter definitions?) { // function1 var Float tauname? = 2*pivalue or expression?; // let2 var Float phiname? = (1 + sqrt(5))/2value or expression?; // let3 return (tau, phi)value or expression?;4 }

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

A literal value is where a value is written 'literally' in the code, such as 3.142 3.142 3.142 3.142 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.

TypeExample of literal
Int3 3 3 3 3
Float2.0 2.0 2.0 2.0 2.0
Booleantrue
String"Hello" "Hello" "Hello" "Hello" "Hello"
Tuple(3, "banana") (3, "banana") (3, "banana") (3, "banana") (3, "banana")
List["lemon", "lime", "orange"] ["lemon", "lime", "orange"] ["lemon", "lime", "orange"] {"lemon", "lime", "orange"} ["lemon", "lime", "orange"]
Dictionary["a":3, "b":5] ["a":3, "b":5] ["a":3, "b":5] ["a":3, "b":5] ["a":3, "b":5]

For more about List and Dictionary literals see the Standard data structures table.

Indexed values

If a named value is of an indexable Type (String str string String String or List list List List List), then you can refer to a contiguous subset (or range) of its values using methods subString and subList respectively.

The upper index of a range is exclusive, so to define a range that picks items indexed from i to j, you would specify the range as (i, j + 1) (i, j + 1) (i, j + 1) (i, j + 1) (i, j + 1).

If the two index values of a range are equal, or the second is smaller than the first, then an empty string or list is returned.

Indexes are used only for reading values. Writing a value to a specific index location in a list is done using method put.

Example of using indexing on a string:

+main 1 variable aname? set to "Hello world!"value or expression?2 call printprocedureName?(a[4]arguments?)3 call printprocedureName?(a.subString(4, a.length())arguments?)4 call printprocedureName?(a.subString(0, 7)arguments?)5 end main
+def main() -> None: 1 aname? = "Hello world!"value or expression? # variable definition2 printprocedureName?(a[4]arguments?)3 printprocedureName?(a.subString(4, a.length())arguments?)4 printprocedureName?(a.subString(0, 7)arguments?)5 main()
+static void main() { 1 var aname? = "Hello world!"value or expression?;2 printprocedureName?(a[4]arguments?);3 printprocedureName?(a.subString(4, a.length())arguments?);4 printprocedureName?(a.subString(0, 7)arguments?);5 }
+Sub main() 1 Dim aname? = "Hello world!"value or expression? ' variable definition2 printprocedureName?(a[4]arguments?)3 printprocedureName?(a.subString(4, a.length())arguments?)4 printprocedureName?(a.subString(0, 7)arguments?)5 End Sub
+static void main() { 1 var aname? = "Hello world!"value or expression?;2 printprocedureName?(a[4]arguments?);3 printprocedureName?(a.subString(4, a.length())arguments?);4 printprocedureName?(a.subString(0, 7)arguments?);5 }




o
o world!
Hello w     (since the upper bound of a range is exclusive)

And on a list:

+main 1 variable liname? set to new List<of Int>()value or expression?2 set livariableName? to [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]value or expression?3 call printprocedureName?(li[4]arguments?)4 call printprocedureName?(li.subList(4, li.length())arguments?)5 call printprocedureName?(li.subList(0, 7)arguments?)6 end main
+def main() -> None: 1 liname? = list[int]()value or expression? # variable definition2 livariableName? = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]value or expression? # re-assign variable3 printprocedureName?(li[4]arguments?)4 printprocedureName?(li.subList(4, li.length())arguments?)5 printprocedureName?(li.subList(0, 7)arguments?)6 main()
+static void main() { 1 var liname? = new List<int>()value or expression?;2 livariableName? = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]value or expression?; // re-assign variable3 printprocedureName?(li[4]arguments?);4 printprocedureName?(li.subList(4, li.length())arguments?);5 printprocedureName?(li.subList(0, 7)arguments?);6 }
+Sub main() 1 Dim liname? = New List(Of Integer)()value or expression? ' variable definition2 livariableName? = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}value or expression? ' re-assign variable3 printprocedureName?(li[4]arguments?)4 printprocedureName?(li.subList(4, li.length())arguments?)5 printprocedureName?(li.subList(0, 7)arguments?)6 End Sub
+static void main() { 1 var liname? = new List<int>()value or expression?;2 livariableName? = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]value or expression?; // re-assign variable3 printprocedureName?(li[4]arguments?);4 printprocedureName?(li.subList(4, li.length())arguments?);5 printprocedureName?(li.subList(0, 7)arguments?);6 }




4     (the value of the list item)
[4, 5, 6, 7, 8, 9, 10, 11]     (a new list)
[0, 1, 2, 3, 4, 5, 6]     (since the upper bound of a range is exclusive)

Operators

Arithmetic operators

Arithmetic operators can be applied to Float float double Double double or Int int int Integer int arguments. The result may be a Float float double Double double or an Int int int Integer int depending on the arguments.

For the + − * operators, the result is a Float float double Double double if either of the arguments is a Float float double Double double, and an Int int int Integer int if both arguments are Int int int Integer int.

For the / operator, at least one of the values must be a Float float double Double double, and the result is always a Float float double Double double. It can be converted to an Int int int Integer int using standalone function floor.

For raising one value to the power of another, use the syntax in these examples:

call printprocedureName?(pow(4, 4)arguments?)0
printprocedureName?(pow(4, 4)arguments?)0
printprocedureName?(pow(4, 4)arguments?);0
printprocedureName?(pow(4, 4)arguments?)0
printprocedureName?(pow(4, 4)arguments?);0

If, when including a power in an expression, it does not parse, try enclosing parts of the expression in brackets to remove potential ambiguity.

The operator mod (integer remainder) is applied only to Int int int Integer int arguments, and the result is Int int int Integer int.

    2/3.0          ⟶  0.666..
    2*3             ⟶  6
    2 + 3          ⟶  5
    2 − 3          ⟶  −1
    11 mod 3    ⟶  2

For integer division, either use method floor on a Float float double Double double result, or use method divAsInt (which returns an Int int int Integer int):

    (11/3.0).floor()       ⟶  3
    (-11/3.0).floor()      ⟶ −4
    divAsInt(11, 3)      ⟶ 3

Note that rounding is always down.

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

When combining mod with other operators in an expression, insert round brackets to avoid ambiguity e.g.:

    (5 + 6) mod 3
(5 + 6) % 3
(5 + 6) % 3
(5 + 6) Mod 3
(5 + 6) % 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

The editor automatically puts spaces around the operators + and −, but not around / or *. This is just to visually reinforce the precedence.

Logical operators

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

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

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

+function exOrname?(a as Boolean, b as Booleanparameter definitions?) returns BooleanType? 1 return a and not b or b and not avalue or expression?2 end function
+def exOrname?(a: bool, b: boolparameter definitions?) -> boolType?: # function1 return a and not b or b and not avalue or expression?2 main()
+static boolType? exOrname?(bool a, bool bparameter definitions?) { // function1 return a && !b || b && !avalue or expression?;2 }
+Function exOrname?(a As Boolean, b As Booleanparameter definitions?) As BooleanType? 1 Return a And Not b Or b And Not avalue or expression?2 End Function
+static boolType? exOrname?(bool a, bool bparameter definitions?) { // function1 return a && !b || b && !avalue or expression?;2 }

String operator

The + perator is also used for concatenating String str string String String values.

Equality testing

Testing equality of Int int int Integer int, Float float double Double double or Boolean bool bool Boolean bool values

Equality testing of these Value Types uses the is and isnt keywords between two values and returns a Boolean bool bool Boolean bool result:

Testing equality of String str string String String values and of all Reference Types

Equality testing of these Reference Types uses common dot methods equals and notEqualTo:

The comparison is made sequentially through the characters or items in the structures to see if the objects are equal. Two Lists compare equal if they contain the same items in the same order. And two instances of the same class compare equal if the values of all their properties compare equal.

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 may be applied between two values of Type Float float double Double double, but any named value or expression that evaluates to an Int int int Integer int may always be used where a Float float double Double double is expected.

These operators cannot be applied to strings. Use the dot methods isBefore and isAfter to compare strings alphabetically. See Dot methods on a String.

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:

set xvariableName? to (a > b) and (b < c)value or expression?0 xvariableName? = (a > b) and (b < c)value or expression? # re-assign variable0 xvariableName? = (a > b) && (b < c)value or expression?; // re-assign variable0 xvariableName? = (a > b) And (b < c)value or expression? ' re-assign variable0 xvariableName? = (a > b) && (b < c)value or expression?; // re-assign variable0
set xvariableName? to (a + b) > (c - d)value or expression?0 xvariableName? = (a + b) > (c - d)value or expression? # re-assign variable0 xvariableName? = (a + b) > (c - d)value or expression?; // re-assign variable0 xvariableName? = (a + b) > (c - d)value or expression? ' re-assign variable0 xvariableName? = (a + b) > (c - d)value or expression?; // re-assign variable0

Function reference

An expression may simply be a reference to a function, or it may include several function references within it. Examples:
+main 1 call printprocedureName?(sin(radians(30))arguments?)2 variable xname? set to pow(sin(radians(30)), 2) + pow(cos(radians(30)), 2)value or expression?3 variable namename? set to input("Your name")value or expression?4 call printprocedureName?(name.upperCase()arguments?)5 end main
+def main() -> None: 1 printprocedureName?(sin(radians(30))arguments?)2 xname? = pow(sin(radians(30)), 2) + pow(cos(radians(30)), 2)value or expression? # variable definition3 namename? = input("Your name")value or expression? # variable definition4 printprocedureName?(name.upperCase()arguments?)5 main()
+static void main() { 1 printprocedureName?(sin(radians(30))arguments?);2 var xname? = pow(sin(radians(30)), 2) + pow(cos(radians(30)), 2)value or expression?;3 var namename? = input("Your name")value or expression?;4 printprocedureName?(name.upperCase()arguments?);5 }
+Sub main() 1 printprocedureName?(sin(radians(30))arguments?)2 Dim xname? = pow(sin(radians(30)), 2) + pow(cos(radians(30)), 2)value or expression? ' variable definition3 Dim namename? = input("Your name")value or expression? ' variable definition4 printprocedureName?(name.upperCase()arguments?)5 End Sub
+static void main() { 1 printprocedureName?(sin(radians(30))arguments?);2 var xname? = pow(sin(radians(30)), 2) + pow(cos(radians(30)), 2)value or expression?;3 var namename? = input("Your name")value or expression?;4 printprocedureName?(name.upperCase()arguments?);5 }

Notes

lambda

A lambda is a lightweight means of defining a function 'inline' and without a name (i.e. an anonymous function). You would typically define a lambda:

The syntax for a lambda is as follows:

Although a lambda is commonly defined 'inline' it is also possible to assign a lambda to a named value, so as to reuse it within the scope of that named value.

Example using a lambda to look for true in a list of Boolean values:
+function liveNeighboursname?(cells as List<of Boolean>, c as Intparameter definitions?) returns IntType? 1 variable neighboursname? set to neighbourCells(c)value or expression?2 variable livename? set to neighbours.filter(lambda i as Int => cells[i])value or expression?3 return live.length()value or expression?4 end function
+def liveNeighboursname?(cells: list[bool], c: intparameter definitions?) -> intType?: # function1 neighboursname? = neighbourCells(c)value or expression? # variable definition2 livename? = neighbours.filter(lambda i: int => cells[i])value or expression? # variable definition3 return live.length()value or expression?4 main()
+static intType? liveNeighboursname?(List<bool> cells, int cparameter definitions?) { // function1 var neighboursname? = neighbourCells(c)value or expression?;2 var livename? = neighbours.filter(lambda int i => cells[i])value or expression?;3 return live.length()value or expression?;4 }
+Function liveNeighboursname?(cells As List(Of Boolean), c As Integerparameter definitions?) As IntegerType? 1 Dim neighboursname? = neighbourCells(c)value or expression? ' variable definition2 Dim livename? = neighbours.filter(lambda i As Integer => cells[i])value or expression? ' variable definition3 Return live.length()value or expression?4 End Function
+static intType? liveNeighboursname?(List<bool> cells, int cparameter definitions?) { // function1 var neighboursname? = neighbourCells(c)value or expression?;2 var livename? = neighbours.filter(lambda int i => cells[i])value or expression?;3 return live.length()value or expression?;4 }

if (expression)

The if expression has two forms, both of which return a value:

  1. Similar syntax to the if instruction, but is an expression that returns a value.
  2. A simpler form that distinguishes only two possibilities, with the format:
    variable isBiggername? set to if(a >= b, true, false)value or expression?0 isBiggername? = if(a >= b, True, False)value or expression? # variable definition0 var isBiggername? = if(a >= b, true, false)value or expression?;0 Dim isBiggername? = if(a >= b, True, False)value or expression? ' variable definition0 var isBiggername? = if(a >= b, true, false)value or expression?;0

An if expression may be used within an if expression but will be hard to read even when enclosed in brackets.

Examples using if expression:

   ● Choice of variable definition assignments (the brackets around the first if expression are optional):

variable cname? set to 1160value or expression?0
cname? = 1160value or expression? # variable definition0
var cname? = 1160value or expression?;0
Dim cname? = 1160value or expression? ' variable definition0
var cname? = 1160value or expression?;0
set c to (if c < 1160 then c + 40 else c - 1160) Code does not parse as Elan.
set c to (if c < 1160 then c + 40 else c - 1160) Code does not parse as Elan.
set c to (if c < 1160 then c + 40 else c - 1160) Code does not parse as Elan.
set c to (if c < 1160 then c + 40 else c - 1160) Code does not parse as Elan.
set c to (if c < 1160 then c + 40 else c - 1160) Code does not parse as Elan.
variable d set to if c < 580 then c - 40 else c + 60 Code does not parse as Elan.
variable d set to if c < 580 then c - 40 else c + 60 Code does not parse as Elan.
variable d set to if c < 580 then c - 40 else c + 60 Code does not parse as Elan.
variable d set to if c < 580 then c - 40 else c + 60 Code does not parse as Elan.
variable d set to if c < 580 then c - 40 else c + 60 Code does not parse as Elan.

   ● Choice in a return instruction:

return if isGreen(attempt, target, n) then setChar(attempt, n, "*") else attempt Code does not parse as Elan.
return if isGreen(attempt, target, n) then setChar(attempt, n, "*") else attempt Code does not parse as Elan.
return if isGreen(attempt, target, n) then setChar(attempt, n, "*") else attempt Code does not parse as Elan.
return if isGreen(attempt, target, n) then setChar(attempt, n, "*") else attempt Code does not parse as Elan.
return if isGreen(attempt, target, n) then setChar(attempt, n, "*") else attempt Code does not parse as Elan.

   ● Using elif clause:

return if attempt[n] is "*" then attempt elif isYellow(attempt, target, n) then setChar(attempt, n, "+") else setChar(attempt, n, "_") Code does not parse as Elan.
return if attempt[n] is "*" then attempt elif isYellow(attempt, target, n) then setChar(attempt, n, "+") else setChar(attempt, n, "_") Code does not parse as Elan.
return if attempt[n] is "*" then attempt elif isYellow(attempt, target, n) then setChar(attempt, n, "+") else setChar(attempt, n, "_") Code does not parse as Elan.
return if attempt[n] is "*" then attempt elif isYellow(attempt, target, n) then setChar(attempt, n, "+") else setChar(attempt, n, "_") Code does not parse as Elan.
return if attempt[n] is "*" then attempt elif isYellow(attempt, target, n) then setChar(attempt, n, "+") else setChar(attempt, n, "_") Code does not parse as Elan.

   ● Using the simpler if expression in a call procedure print:

+main 1 variable pname? set to unicode(0x03c0)value or expression?2 call testPiprocedureName?(p, 3arguments?)3 call testPiprocedureName?(p, 4arguments?)4 end main +procedure testPiname?(p as String, v as Floatparameter definitions?) 5 call printprocedureName?(if(pi > v, $"so, {p} > {v}", $"and {p} < {v}")arguments?)6 end procedure
+def main() -> None: 1 pname? = unicode(0x03c0)value or expression? # variable definition2 testPiprocedureName?(p, 3arguments?) # call procedure3 testPiprocedureName?(p, 4arguments?) # call procedure4 +def testPiname?(p: str, v: floatparameter definitions?) -> None: # procedure5 printprocedureName?(if(pi > v, f"so, {p} > {v}", f"and {p} < {v}")arguments?)6 main()
+static void main() { 1 var pname? = unicode(0x03c0)value or expression?;2 testPiprocedureName?(p, 3arguments?); // call procedure3 testPiprocedureName?(p, 4arguments?); // call procedure4 } +static void testPiname?(string p, double vparameter definitions?) { // procedure5 printprocedureName?(if(pi > v, $"so, {p} > {v}", $"and {p} < {v}")arguments?);6 }
+Sub main() 1 Dim pname? = unicode(&H03c0)value or expression? ' variable definition2 testPiprocedureName?(p, 3arguments?) ' call procedure3 testPiprocedureName?(p, 4arguments?) ' call procedure4 End Sub +Sub testPiname?(p As String, v As Doubleparameter definitions?) ' procedure5 printprocedureName?(if(pi > v, $"so, {p} > {v}", $"and {p} < {v}")arguments?)6 End Sub
+static void main() { 1 var pname? = unicode(0x03c0)value or expression?;2 testPiprocedureName?(p, 3arguments?); // call procedure3 testPiprocedureName?(p, 4arguments?); // call procedure4 } +static void testPiname?(String p, double vparameter definitions?) { // procedure5 printprocedureName?(arguments?);6 }
would print these lines to the display:

   so π > 3
   and π < 4

new

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

variable headRefname? set to new AsRef<of List<of Int>>(head)value or expression?0
headRefname? = AsRef[list[int]](head)value or expression? # variable definition0
var headRefname? = new AsRef<List<int>>(head)value or expression?;0
Dim headRefname? = New AsRef(Of List(Of Integer))(head)value or expression? ' variable definition0
var headRefname? = new AsRef<List<int>>(head)value or expression?;0

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.

A comment is entered at any editor prompt by typing the hash symbol # which then provides a field in which you may enter text, or leave blank. Your text may contain any characters, except that it must not start with an open square bracket [.

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 comment comment comment comment comment option, namely a # symbol.

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 procedure 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 procedure 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 str string String String, List list List List List and Dictionary Dictionary Dictionary Dictionary Dictionary.

Cannot range ...

A range may be applied only to certain data structure Types: String str string String String and List list List List List.

Cannot new ...

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

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 call extension method directly

A method that is defined within the Library as an extension method, such as toString, 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 List list List List List, must specify the Type of their members, for example List list List List List<of Int int int Integer 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 Dictionary Dictionary Dictionary Dictionary<of String str string String String, Float float double Double double>.

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 Dictionary Dictionary Dictionary Dictionary with one or more duplicated keys in the definition.

Library or class function ... cannot be used without brackets

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, global, if, image, import, in, inherits, interface, is, isnt, lambda, let, library, 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 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, but not by merely changing the case of part of the name.

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:
action, arguments, array, async, await, boolean, break, by, byte, case, char, const, continue, curry, debugger, default, delete, dictionary, do, double, error, eval, export, extends, final, finally, float, goto, ignore, implements, instanceof, int, into, list, long, match, myclass, namespace, native, null, on, optional, otherwise, package, partial, pattern, protected, public, short, static, string, super, switch, system, synchronized, throws, todo, transient, tuple, typeof, void, volatile, var, when, xor, yield

Index cannot be negative.

An index into a 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 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.

... 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.

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:

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 variable definition instruction and then use the named value in the 'actual' field of the assert equal instruction. Some more complex expressions are permissible, but these two restrictions apply:

'variable name' field in a set instruction

The first field in a change variable instruction most commonly takes the name of an existing variable.

'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

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 list List List List<of Int int int Integer int>
Dictionary Dictionary Dictionary Dictionary Dictionary<of String str string String String, Int int int Integer 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 instruction is most commonly a simple name. Less commonly, it may take the form of a dconstruction of a List list List List List or Tuple.

Advisory

Code change suggested. Method was deprecated in v7.1.

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 variable definition instructions; otherwise it might become impossible to add more text.

ReferenceError: Cannot access '[name]' before initialisation


Elan Language Reference go to the top