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.
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
constant 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? # constant7
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
const String 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
Const rname? = pointer.value()value or expression?7
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
final String rname? = pointer.value()value or expression?; // constant7
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 always returns a value.
- a function can have no side effects (i.e. no references outside its code) and cannot depend on any System methods.
- a function's execution cannot be interrupted by any external event, though a a non-terminating loop in a function may be detected with tests that time out.
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 instructions show whether your assertions pass or fail.
Example of a set of assert instructions that test a function
▶ Test function binarySearch on four test lists
test test_binarySearch
variable li1 set to ["lemon", "lime", "orange"]
assert binarySearch(li1, "lemon") is true pass
assert binarySearch(li1, "lime") is true pass
assert binarySearch(li1, "orange") is true pass
assert binarySearch(li1, "pear") is false pass
variable li2 set to ["lemon", "orange"]
assert binarySearch(li2, "lemon") is true pass
assert binarySearch(li2, "orange") is true pass
assert binarySearch(li2, "pear") is false pass
variable li3 set to ["lemon"]
assert binarySearch(li3, "lemon") is true pass
assert binarySearch(li3, "lime") is false pass
variable li4 set to new List()
assert binarySearch(li4, "pear") is false pass
end test
Code does not parse as Elan.
test test_binarySearch
variable li1 set to ["lemon", "lime", "orange"]
assert binarySearch(li1, "lemon") is true pass
assert binarySearch(li1, "lime") is true pass
assert binarySearch(li1, "orange") is true pass
assert binarySearch(li1, "pear") is false pass
variable li2 set to ["lemon", "orange"]
assert binarySearch(li2, "lemon") is true pass
assert binarySearch(li2, "orange") is true pass
assert binarySearch(li2, "pear") is false pass
variable li3 set to ["lemon"]
assert binarySearch(li3, "lemon") is true pass
assert binarySearch(li3, "lime") is false pass
variable li4 set to new List()
assert binarySearch(li4, "pear") is false pass
end test
Code does not parse as Elan.
test test_binarySearch
variable li1 set to ["lemon", "lime", "orange"]
assert binarySearch(li1, "lemon") is true pass
assert binarySearch(li1, "lime") is true pass
assert binarySearch(li1, "orange") is true pass
assert binarySearch(li1, "pear") is false pass
variable li2 set to ["lemon", "orange"]
assert binarySearch(li2, "lemon") is true pass
assert binarySearch(li2, "orange") is true pass
assert binarySearch(li2, "pear") is false pass
variable li3 set to ["lemon"]
assert binarySearch(li3, "lemon") is true pass
assert binarySearch(li3, "lime") is false pass
variable li4 set to new List()
assert binarySearch(li4, "pear") is false pass
end test
Code does not parse as Elan.
test test_binarySearch
variable li1 set to ["lemon", "lime", "orange"]
assert binarySearch(li1, "lemon") is true pass
assert binarySearch(li1, "lime") is true pass
assert binarySearch(li1, "orange") is true pass
assert binarySearch(li1, "pear") is false pass
variable li2 set to ["lemon", "orange"]
assert binarySearch(li2, "lemon") is true pass
assert binarySearch(li2, "orange") is true pass
assert binarySearch(li2, "pear") is false pass
variable li3 set to ["lemon"]
assert binarySearch(li3, "lemon") is true pass
assert binarySearch(li3, "lime") is false pass
variable li4 set to new List()
assert binarySearch(li4, "pear") is false pass
end test
Code does not parse as Elan.
test test_binarySearch
variable li1 set to ["lemon", "lime", "orange"]
assert binarySearch(li1, "lemon") is true pass
assert binarySearch(li1, "lime") is true pass
assert binarySearch(li1, "orange") is true pass
assert binarySearch(li1, "pear") is false pass
variable li2 set to ["lemon", "orange"]
assert binarySearch(li2, "lemon") is true pass
assert binarySearch(li2, "orange") is true pass
assert binarySearch(li2, "pear") is false pass
variable li3 set to ["lemon"]
assert binarySearch(li3, "lemon") is true pass
assert binarySearch(li3, "lime") is false pass
variable li4 set to new List()
assert binarySearch(li4, "pear") is false pass
end test
Code does not parse as Elan.
Notes
- Elan tests are designed to test functions only. It is not possible to call a procedure or main routine within a test.
Nor is it possible to use any System method (the same rule as for a function).
- A test may optionally be given a name or description in free-form text, just like a comment, which plays no role in the execution of the test.
You might give the test the same name as a function that it is testing, or you might describe a particular scenario that is being tested.
- test instructions may be written anywhere in the code, provided they are at the global level.
- A test instruction may contain any number of assert instructions. The test runner is part of the IDE and it automatically attempts to run all assert instructions showing each one's pass or fail outcome alongside. However, if the test hits a runtime error (as distinct from an assert failure) then execution of the test will stop and remaining asserts will be shown as 'not run'.
- In addition to assert instructions, a test may contain any other instructions that may be added into a function.
- All assert statements should be at the top level within the test instruction; none may be put into a loop structure.
Testing Float values
When testing Float values it is recommend that you use the round method to round the computed result to a fixed number of decimal places. This avoids rounding errors and is easier to read. For example:
test test_round
assert sqrt(2).round(3) is 1.414 pass
end test
Code does not parse as Elan.
test test_round
assert sqrt(2).round(3) is 1.414 pass
end test
Code does not parse as Elan.
test test_round
assert sqrt(2).round(3) is 1.414 pass
end test
Code does not parse as Elan.
test test_round
assert sqrt(2).round(3) is 1.414 pass
end test
Code does not parse as Elan.
test test_round
assert sqrt(2).round(3) is 1.414 pass
end test
Code does not parse as Elan.
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:
In the last assert shown below, note how testing can also be done against an expected error message.
If there are failures, mark the tests that you added since the last successful test as ghosted and then remove their ghosted status one by one until the cause is identified and fixed.
test test_list
variable a set to [5, 1, 7]
assert a[0] is 5 pass
assert a[2] is 7 pass
assert a[4] is 0 actual (computed): Out of range index: 4 size: 3
assert a[4] is "Out of range index: 4 size: 3" pass
end test
Code does not parse as Elan.
test test_list
variable a set to [5, 1, 7]
assert a[0] is 5 pass
assert a[2] is 7 pass
assert a[4] is 0 actual (computed): Out of range index: 4 size: 3
assert a[4] is "Out of range index: 4 size: 3" pass
end test
Code does not parse as Elan.
test test_list
variable a set to [5, 1, 7]
assert a[0] is 5 pass
assert a[2] is 7 pass
assert a[4] is 0 actual (computed): Out of range index: 4 size: 3
assert a[4] is "Out of range index: 4 size: 3" pass
end test
Code does not parse as Elan.
test test_list
variable a set to [5, 1, 7]
assert a[0] is 5 pass
assert a[2] is 7 pass
assert a[4] is 0 actual (computed): Out of range index: 4 size: 3
assert a[4] is "Out of range index: 4 size: 3" pass
end test
Code does not parse as Elan.
test test_list
variable a set to [5, 1, 7]
assert a[0] is 5 pass
assert a[2] is 7 pass
assert a[4] is 0 actual (computed): Out of range index: 4 size: 3
assert a[4] is "Out of range index: 4 size: 3" pass
end test
Code does not parse as Elan.
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; }"value or expression?0
sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"value or expression? # constant0
const Unknown sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"value or expression?;0
Const sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"value or expression?0
final Unknown sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"value or expression?; // constant0
Ignoring tests
To ignore tests, use ghosting.
Even when tests or asserts are ghosted, all the tests will be run and their status shown, but
the overall test status will show the status of only the unghosted tests (green pass, amber warning or red fail).
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.
class
A 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 Class
A Class is instantiated using the keyword class followed by the Class name, and optional inherits to refer to a 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 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?4
namesname?: list[str]Type? # property5
+def __init__(self: Thing) -> None:
6
self.namesvariableName? = createList(6, "X")value or expression? # change variable7
+def toStringname?(self: Thing) -> strType?:
# function8
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?; // change variable7
}
+public stringType? toStringname?(parameter definitionsparameter definitions?) {
// function8
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?4
Property namesname? As List(Of String)Type?5
+Sub New(parameter definitionsparameter definitions?)
6
Me.namesvariableName? = createList(6, "X")value or expression? ' change 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? {4
public List<String>Type? namesname?; // property5
+public Thing(parameter definitionsparameter definitions?) {
6
this.namesvariableName? = createList(6, "X")value or expression?; // change variable7
}
+public StringType? toStringname?(parameter definitionsparameter definitions?) {
// function8
return ""value or expression?;9
}
}
Here is an example of class definition taken from demo program snake - object-oriented:
Example 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
constant ranXname? set to randint(0, 39)value or expression?10
constant 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?1
+def __init__(self: Apple) -> None:
2
self.locationvariableName? = Square(0, 0)value or expression? # change variable3
+def toStringname?(self: Apple) -> strType?:
# function4
return ""value or expression?5
locationname?: SquareType? # property6
+def newRandomPositionname?(self: Apple, snake: Snakeparameter definitions?) -> None:
# procedure7
changePositionname? = Truevalue or expression? # variable definition8
+while changePositioncondition?:
9
ranXname? = randint(0, 39)value or expression? # constant10
ranYname? = randint(0, 29)value or expression? # constant11
self.locationvariableName? = Square(ranX, ranY)value or expression? # change variable12
+if not snake.bodyCovers(self.location)condition?:
13
changePositionvariableName? = Falsevalue or expression? # change variable14
+def updateBlocksname?(self: Apple, blocks: list[list[int]]parameter definitions?) -> None:
# procedure15
blocks[self.location.x][self.location.y]variableName? = redvalue or expression? # change variable16
main()
+class AppleName? inheritance? {1
+public Apple(parameter definitionsparameter definitions?) {
2
this.locationvariableName? = new Square(0, 0)value or expression?; // change variable3
}
+public stringType? toStringname?(parameter definitionsparameter definitions?) {
// function4
return ""value or expression?;5
}
public SquareType? locationname? {get; private set;} // property6
+public void newRandomPositionname?(Snake snakeparameter definitions?) {
// procedure7
var changePositionname? = truevalue or expression?;8
+while (changePositioncondition?) {
9
const Int ranXname? = randint(0, 39)value or expression?;10
const Int ranYname? = randint(0, 29)value or expression?;11
this.locationvariableName? = new Square(ranX, ranY)value or expression?; // change variable12
+if (!snake.bodyCovers(this.location)condition?) {
13
changePositionvariableName? = falsevalue or expression?; // change variable14
}
}
}
+public void updateBlocksname?(List<List<int>> blocksparameter definitions?) {
// procedure15
blocks[this.location.x][this.location.y]variableName? = redvalue or expression?; // change variable16
}
}
+Class AppleName?1
+Sub New(parameter definitionsparameter definitions?)
2
Me.locationvariableName? = New Square(0, 0)value or expression? ' change 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?)
' procedure7
Dim changePositionname? = Truevalue or expression? ' variable definition8
+While changePositioncondition?
9
Const ranXname? = randint(0, 39)value or expression?10
Const ranYname? = randint(0, 29)value or expression?11
Me.locationvariableName? = New Square(ranX, ranY)value or expression? ' change variable12
+If Not snake.bodyCovers(Me.location)condition? Then
13
changePositionvariableName? = Falsevalue or expression? ' change variable14
End If
End While
End Sub
+Sub updateBlocksname?(blocks As List(Of List(Of Integer))parameter definitions?)
' procedure15
blocks[Me.location.x][Me.location.y]variableName? = redvalue or expression? ' change variable16
End Sub
End Class
+class AppleName? {1
+public Apple(parameter definitionsparameter definitions?) {
2
this.locationvariableName? = new Square(0, 0)value or expression?; // change variable3
}
+public StringType? toStringname?(parameter definitionsparameter definitions?) {
// function4
return ""value or expression?;5
}
public SquareType? locationname?; // property6
+public void newRandomPositionname?(Snake snakeparameter definitions?) {
// procedure7
var changePositionname? = truevalue or expression?;8
+while (changePositioncondition?) {
9
final Int ranXname? = randint(0, 39)value or expression?; // constant10
final Int ranYname? = randint(0, 29)value or expression?; // constant11
this.locationvariableName? = new Square(ranX, ranY)value or expression?; // change variable12
+if (!snake.bodyCovers(this.location)condition?) {
13
changePositionvariableName? = falsevalue or expression?; // change variable14
}
}
}
+public void updateBlocksname?(List<List<int>> blocksparameter definitions?) {
// procedure15
blocks[this.location.x][this.location.y]variableName? = redvalue or expression?; // change variable16
}
}
this
If in the code of Class
A you want to invoke a method in Class
S and pass to it the current instance of
A,
you can refer to this instance with the keyword
this, as shown by this line in demo program
snake - 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 which needs to be passed an instance of Class Snake.
(apple is an instance of Class Apple).
inherits
An ordinary Class (also known as a concrete class) may optionally inherit from just one abstract class
but may additionally inherit from any number of interfaces.
The concrete Class must define for itself a concrete implementation of every abstract member defined in the abstract class or any interfaces that it inherits from, directly or indirectly.
Notes
- An abstract class must be declared in the code above any Class that inherits from it.
- The abstract class (if any) and the interfaces (if any) that a concrete class inherits from may not contain duplicates of any abstract member. Any duplicated definitions in the hierarchy will result in a compile error. If such duplications arise, you should factor out the common member definitions, and move them up the hierarchy or into new interfaces inherited by the interfaces and/or classes that need them.
- Inheritance hierarchies must form a tree, that is you must avoid creating a circular dependency where, for example, Type A inherits from Type B, which inherits from Type C, which inherits from Type A.
- The various 'super-Types' (abstract class and interface) that a concrete class inherits from must not define conflicting members, e.g. members with the same name but having different Type signatures.
- See also example in Interface
abstract class
An abstract class may not be instantiated (and hence may not define a constructor). It may define concrete members i.e.:
- a property
- a function
- a procedure
As with a concrete Class, any of these members may be made private, after the corresponding 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 instruction. For example:
abstract function calculateDiscount() as Float
Code does not parse as Elan.
abstract function calculateDiscount() as Float
Code does not parse as Elan.
abstract function calculateDiscount() as Float
Code does not parse as Elan.
abstract function calculateDiscount() as Float
Code does not parse as Elan.
abstract function calculateDiscount() as Float
Code does not parse as Elan.
If you wish to have several subclasses of an abstract class that share a common implementation for a method, but require that some of the subclasses can define a different implementation, then you should:
- Define the method as abstract on the superclass.
- Define a concrete implementation on the superclass with a similar, but slightly different, name e.g. by adding a prefix such as: default.
- Each subclass must then define its implementation of the abstract method, but the ones needing a common implementation can be just one line, delegating responsibility up to the 'default' method on the superclass.
interface
An interface is similar to an abstract class, with the difference that it may define only abstract members. The advantage of using an interface instead of an abstract class is that a concrete class can inherit from multiple interfaces.
An interface may inherit only from other interfaces.
Important: An interface must not re-declare abstract interfaces that are defined in any interface it inherits from, directly or indirectly.
Example using interface
▶ Example from a version of program blackjack.elan of:
- inheritance from interface Player to abstract class Automated
and to class HumanPlayer, and
- inheritance from abstract class Automated to concrete class Dealer
- (concrete) definitions of function getAction and procedure changeScoreBy following their
abstract declarations.
interface Player
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 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
then Action.draw
else Action.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.
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 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
then Action.draw
else Action.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.
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 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
then Action.draw
else Action.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.
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 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
then Action.draw
else Action.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.
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 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
then Action.draw
else Action.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 (global)
There are two uses of the constant instruction: this one at global level, the other local in a routine and described under constant (local).
A global 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 global constant is defined by a literal of an immutable value Type, namely Int, Float, Boolean or
String.
A 'constant' data structure can be defined by using a function which is then referenced to make the structure available in a variable. See Constant data structure.
Global 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 global constant can be defined by reference to a previously defined global 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 global 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 warningMsgname? set to "Limit reached"literal value or data structure?6
+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
+warningMsgname? = "Limit reached"literal value or data structure? # constant6
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 String warningMsgname? = "Limit reached"literal value or data structure?6
+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 warningMsgname? = "Limit reached"literal value or data structure?6
+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 String warningMsgname? = "Limit reached"literal value or data structure? // constant6
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 and a constant 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.
You define it with a Type name (so starting with an uppercase letter) followed by a
number of values (which must be valid identifiers).
A reference to an enum by its Type name necessarily holds one of the values.
Reference to the value in an enum is by using dot syntax enumType.enumValue.
An enum is read-only: once it has been defined it is not possible to add, remove, or update its values.
Examples of definition and use:
enum Suit spades, hearts, diamonds, clubs ? an enum for use in the following constant colours
enum Action stand, draw
enum Outcome undecided, win, lose, draw, winDouble
enum Status pending, playing, standing, blackjack, bust
main
variable status set to Status.pending
end main
Code does not parse as Elan.
enum Suit spades, hearts, diamonds, clubs ? an enum for use in the following constant colours
enum Action stand, draw
enum Outcome undecided, win, lose, draw, winDouble
enum Status pending, playing, standing, blackjack, bust
main
variable status set to Status.pending
end main
Code does not parse as Elan.
enum Suit spades, hearts, diamonds, clubs ? an enum for use in the following constant colours
enum Action stand, draw
enum Outcome undecided, win, lose, draw, winDouble
enum Status pending, playing, standing, blackjack, bust
main
variable status set to Status.pending
end main
Code does not parse as Elan.
enum Suit spades, hearts, diamonds, clubs ? an enum for use in the following constant colours
enum Action stand, draw
enum Outcome undecided, win, lose, draw, winDouble
enum Status pending, playing, standing, blackjack, bust
main
variable status set to Status.pending
end main
Code does not parse as Elan.
enum Suit spades, hearts, diamonds, clubs ? an enum for use in the following constant colours
enum Action stand, draw
enum Outcome undecided, win, lose, draw, winDouble
enum Status pending, playing, standing, blackjack, bust
main
variable status set to Status.pending
end main
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 will offer a context appropriate subset of the following member instructions:
abstract property,
abstract function,
abstract procedure,
property,
function method,
procedure method and
constructor,
together with these 'private' versions:
private property,
private function and
private procedure.
This table shows which kinds of property, function, procedure and constructor are applicable to the various kinds of Class.
abstract property
An abstract property may be defined only on an abstract class. Any concrete subclass must then implement a concrete (regular) property to match.
abstract function
An abstract function method may be defined only on an abstract class. Any concrete subclass must then implement a concrete (regular) function to match.
abstract procedure
An abstract procedure method may be defined only on an abstract class. Any concrete subclass must then implement a concrete (regular) procedure to match.
property
A property is a named value defined on a class with a name conforming to the rules for an identifier,
and a Type (which may be another class name), for example:
+class NameType? inheritance?1
property heightname? as IntType?2
property boardname? as BoardType?3
property headname? as SquareType?4
property bodyname? as List<of Square>Type?5
+constructor(parameter definitions?)
6
new code
end constructor
+function toStringname?(parameter definitions?) returns StringType?
7
return "undefined"value or expression?8
end function
end class
+class NameType? inheritance?1
heightname?: intType? # property2
boardname?: BoardType? # property3
headname?: SquareType? # property4
bodyname?: list[Square]Type? # property5
+def __init__(self: ) -> None:
6
new code
+def toStringname?(self: ) -> strType?:
# function7
return "undefined"value or expression?8
main()
+class NameType? inheritance? {1
public intType? heightname? {get; private set;} // property2
public BoardType? boardname? {get; private set;} // property3
public SquareType? headname? {get; private set;} // property4
public List<Square>Type? bodyname? {get; private set;} // property5
+public (parameter definitions?) {
6
new code
}
+public stringType? toStringname?(parameter definitions?) {
// function7
return "undefined"value or expression?;8
}
}
+Class NameType?1
Property heightname? As IntegerType?2
Property boardname? As BoardType?3
Property headname? As SquareType?4
Property bodyname? As List(Of Square)Type?5
+Sub New(parameter definitions?)
6
new code
End Sub
+Function toStringname?(parameter definitions?) As StringType?
7
Return "undefined"value or expression?8
End Function
End Class
+class NameType? {1
public intType? heightname?; // property2
public BoardType? boardname?; // property3
public SquareType? headname?; // property4
public List<Square>Type? bodyname?; // property5
+public (parameter definitions?) {
6
new code
}
+public StringType? toStringname?(parameter definitions?) {
// function7
return "undefined"value or expression?;8
}
}
It may be given an initial value within a constructor.
A property may be read, but not written to.
Properties may be modified only from outside the Class by means of a
Procedure method.
function method
A function method follows the same syntax and rules as a global function. The differences are:
- A function method is always referenced (used) by code outside the Class using dot syntax on an instance.
- A function method may directly reference (read only) any property defined on the Class as though it were a variable or parameter.
- A function method may be marked private, in which case it is visible only to code within the Class and, if defined on an abstract class, within its subclasses.
This is done by selecting the property instruction and then keying Ctrl+p. (Pressing these keys again will remove the private modifier).
- toString method is just a regular function method with the specific name, takes no parameters and and returns a value of type String.
If defined for a Class then, if an instance of the Class is printed, function method toString will automatically be used.
Typically toString will return a string made up of one or more of the property values, perhaps with additional text, or the results of function calls.
procedure method
A 'procedure method' follows the same syntax and rules as a global procedure. The differences are:
- A procedure method, like a function method, is always referenced (used) by code outside the Class using dot syntax on an instance.
- A procedure method may read, or write to, any property defined on the Class.
- A procedure method may be marked private, in which case it is visible only to code within the Class and, if defined on an abstract class, within its subclasses.
This is done by selecting the property instruction and then keying Ctrl+p. (Pressing these keys again will remove the private modifier).
private (property, function, procedure)
A property may be marked private, in which case it is visible only to code within the Class and,
if defined on an abstract class, within its subclasses.
This is done by using the context menu on the property instruction or selecting it and keying Ctrl+p.
This action is a toggle used both to set and to remove the modifier private.
- A property that is not private may be read, but not written to.
- A property that is private may
- Whenever you wish to access a property from within a method (or from within the constructor) on the same class, then the name of the property must be prefixed with the 'qualifier': property. ('property-dot'). This applies whether you are reading or setting the property. By this means you can have a method parameter with the same name as a property, but they are unambiguous, because the property must be prefixed. A common pattern is to use the same name in a 'setter' method, for example:
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.
constructor
A (concrete) Class may have an optional constructor so as to:
- initialise properties with fixed values
- define parameters which are used to initialise properties
If a Class does define a constructor, and the constructor defines parameters, then when the Class is instantiated (using new) the values of
the correct Type must be provided. For example, if the Class Square has this constructor:
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
Statement instructions
Statement instructions (sometimes referred to as 'statements') are the imperative keywords used in the methods (procedural logic) of a program, and are:
assert,
call,
constant,
for,
if,
set,
throw,
try,
variable,
while.
assert
The assert 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 instruction.
call
The call instruction is used when you want to run a procedure.
The procedure may be:
- a procedure that you have defined at the global level
call fillRandomprocedureName?(gridarguments?)0
fillRandomprocedureName?(gridarguments?) # call procedure0
fillRandomprocedureName?(gridarguments?); // call procedure0
fillRandomprocedureName?(gridarguments?) ' call procedure0
fillRandomprocedureName?(gridarguments?); // call procedure0
- a procedure method on an object of a Class that you have defined
call apple.newRandomPositionprocedureName?(snakearguments?)0
apple.newRandomPositionprocedureName?(snakearguments?) # call procedure0
apple.newRandomPositionprocedureName?(snakearguments?); // call procedure0
apple.newRandomPositionprocedureName?(snakearguments?) ' call procedure0
apple.newRandomPositionprocedureName?(snakearguments?); // call procedure0
call property.hand.draw()
Code does not parse as Elan.
call property.hand.draw()
Code does not parse as Elan.
call property.hand.draw()
Code does not parse as Elan.
call property.hand.draw()
Code does not parse as Elan.
call property.hand.draw()
Code does not parse as Elan.
- a procedure method that belongs to the same Class as the procedure that you are calling from
call updateNeighboursprocedureName?(arguments?)0
updateNeighboursprocedureName?(arguments?) # call procedure0
updateNeighboursprocedureName?(arguments?); // call procedure0
updateNeighboursprocedureName?(arguments?) ' call procedure0
updateNeighboursprocedureName?(arguments?); // call procedure0
- a procedure provided by the standard library
call sleep_msprocedureName?(2000arguments?)0
sleep_msprocedureName?(2000arguments?) # call procedure0
sleep_msprocedureName?(2000arguments?); // call procedure0
sleep_msprocedureName?(2000arguments?) ' call procedure0
sleep_msprocedureName?(2000arguments?); // call procedure0
- a procedure method on an object of a Type provided by the standard library
call vg.appendprocedureName?(rectarguments?)0
vg.appendprocedureName?(rectarguments?) # call procedure0
vg.appendprocedureName?(rectarguments?); // call procedure0
vg.appendprocedureName?(rectarguments?) ' call procedure0
vg.appendprocedureName?(rectarguments?); // call procedure0
call property.cards.append(card)
Code does not parse as Elan.
call property.cards.append(card)
Code does not parse as Elan.
call property.cards.append(card)
Code does not parse as Elan.
call property.cards.append(card)
Code does not parse as Elan.
call property.cards.append(card)
Code does not parse as Elan.
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:
- either a mutable data structure (List or Dictionary)
- or a Ref reference to a named value Type
- or a Ref reference to an immutable data structure (Set, Stack or Queue)
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 instructions are not allowed in functions.
There is a limit to the complexity of a call 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 set instruction on the line above.
See the error message explanation for 'procedureName' in a call instruction.
constant (local)
There are two uses of the constant instruction: this one local to a routine, the other global to the program and described under constant (global).
The local constant instruction declares and initialises an immutable named valued. The name defined must be a valid identifier.
The initial value is given by a following literal or expression as in these examples:
constant tauname? set to 2*pivalue or expression?0
tauname? = 2*pivalue or expression? # constant0
const Unknown tauname? = 2*pivalue or expression?;0
Const tauname? = 2*pivalue or expression?0
final Unknown tauname? = 2*pivalue or expression?; // constant0
Like a variable, a constant may be used only within a routine, but unlike a variable its value may not be changed with a set instruction.
It is recommended that you always use constant in preference to variable, unless you need to be able to re-assign its value.
You can define a constant in a loop, so that the named value gets a new value each time it is executed, but the value of the variable cannot be changed any other way.
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. See Constant data structures.
for
The construct for p in q specifies looping through a sequence of
characters in a String or items in a List.
The forms of the for loop are:
- The loop variable p is of Type String and the subject q is of Type String:
main
main
main
main
main
for ch in title
call print(ch)
end for
end main
puts each character in title in turn into String ch.
- The loop variable p is of the Type of a List's items (here Int):
new code
new code
main()
new code
new code
new code
puts each character in title in turn into String ch.
- The loop variable p is of the Type of a List's items (here Int):
main
main
main
main
main
for n in primes
call print(n)
end for
end main
puts each item in the List primes in turn into Int n.
- The loop variable p is of the Type of a List's items (here String):
new code
new code
main()
new code
new code
new code
puts each item in the List primes in turn into Int n.
- The loop variable p is of the Type of a List's items (here String):
set basketvariableName? to ["apple", "banana", "cherry", "date"]value or expression?0
basketvariableName? = ["apple", "banana", "cherry", "date"]value or expression? # change variable0
basketvariableName? = ["apple", "banana", "cherry", "date"]value or expression?; // change variable0
basketvariableName? = {"apple", "banana", "cherry", "date"}value or expression? ' change variable0
basketvariableName? = ["apple", "banana", "cherry", "date"]value or expression?; // change variable0
for fruit in basket
Code does not parse as Elan.
for fruit in basket
Code does not parse as Elan.
for fruit in basket
Code does not parse as Elan.
for fruit in basket
Code does not parse as Elan.
for fruit in basket
Code does not parse as Elan.
puts each item in the List basket in turn into String fruit.
Alternatively, the selection can be constrained using methods range and rangeInSteps to specify integer values of p:
- The loop variable p's value is limited to a range:
+main
1
variable numbersname? set to [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]value or expression?2
+for nitem? in range(3, 8)source?
3
call printprocedureName?(narguments?)4
end for
end main
+def main() -> None:
1
numbersname? = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]value or expression? # variable definition2
+for nitem? in range(3, 8)source?:
3
printprocedureName?(narguments?)4
main()
+static void main() {
1
var numbersname? = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]value or expression?;2
+foreach (nitem? in range(3, 8)source?) {
3
printprocedureName?(narguments?);4
}
}
+Sub main()
1
Dim numbersname? = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}value or expression? ' variable definition2
+For Each nitem? In range(3, 8)source?
3
printprocedureName?(narguments?)4
Next n
End Sub
+static void main() {
1
var numbersname? = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]value or expression?;2
+foreach (nitem? in range(3, 8)source?) {
3
printprocedureName?(narguments?);4
}
}
gives values with which to index into the List numbers.
The loop variable p does not have to have been previously declared or initialised.
Examples using for
▶ Function to reverse a 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? # change 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?; // change 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? ' change 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?; // change variable4
}
return sReturnvalue or expression?;5
}
▶ To print the first item in a 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:
+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.
if (instruction)
The if..then..else..elif.. construct specifies which of several code sequences is to be executed next.
- It is structured like this (elif is the Elan keyword for 'else if'):
if condition then
instructions
elif condition then
instructions
else
instructions
end if
- The conditions are Boolean values or expressions.
- elif and else clauses are optional.
- You can add as many elif clauses as you wish, but only one unconditional else which, if present, must be last.
See also if (expression) for using the related if to return a value.
Examples using if 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:
if item.equals(value) then
set result to true
elif item.isBefore(value) then
set result to binarySearch(li[:mid], item)
else
set result to binarySearch(li[mid + 1:], item)
end if
Code does not parse as Elan.
if item.equals(value) then
set result to true
elif item.isBefore(value) then
set result to binarySearch(li[:mid], item)
else
set result to binarySearch(li[mid + 1:], item)
end if
Code does not parse as Elan.
if item.equals(value) then
set result to true
elif item.isBefore(value) then
set result to binarySearch(li[:mid], item)
else
set result to binarySearch(li[mid + 1:], item)
end if
Code does not parse as Elan.
if item.equals(value) then
set result to true
elif item.isBefore(value) then
set result to binarySearch(li[:mid], item)
else
set result to binarySearch(li[mid + 1:], item)
end if
Code does not parse as Elan.
if item.equals(value) then
set result to true
elif item.isBefore(value) then
set result to binarySearch(li[:mid], item)
else
set result to binarySearch(li[mid + 1:], item)
end if
Code does not parse as Elan.
set
The set..to.. instruction assigns a new value to an existing variable (mutable) named value.
The new value must be of the same Type as (or a Type compatible with) that of the variable.
A set 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,
which then allows use of the dot method set on it. See Call by reference
throw (exception)
You can deliberately generate, or 'throw', an error exception when a specific circumstance is identified, using a throw instruction
which defines an explanatory string.
For the program to retain control when an exception is raised, use the try instruction.
Example using throw
▶ 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%
Error
try
You can enclose code in a try..catch 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..catch instruction.
Examples using try..catch
▶ Use a test to catch an error in a function:
test test_try_1
try
call foo()
call print("not caught")
catch exception in e
call print(e)
end try
end test
Code does not parse as Elan.
test test_try_1
try
call foo()
call print("not caught")
catch exception in e
call print(e)
end try
end test
Code does not parse as Elan.
test test_try_1
try
call foo()
call print("not caught")
catch exception in e
call print(e)
end try
end test
Code does not parse as Elan.
test test_try_1
try
call foo()
call print("not caught")
catch exception in e
call print(e)
end try
end test
Code does not parse as Elan.
test test_try_1
try
call foo()
call print("not caught")
catch exception in e
call print(e)
end try
end test
Code does not parse as Elan.
▶ To check an error message and, if unexpected, throw the exception up:
test test_try_2
try
call foo()
call print("not caught")
catch exception in e
if not (e.equals("an expected message")) then
throw exception e
end if
end try
end test
Code does not parse as Elan.
test test_try_2
try
call foo()
call print("not caught")
catch exception in e
if not (e.equals("an expected message")) then
throw exception e
end if
end try
end test
Code does not parse as Elan.
test test_try_2
try
call foo()
call print("not caught")
catch exception in e
if not (e.equals("an expected message")) then
throw exception e
end if
end try
end test
Code does not parse as Elan.
test test_try_2
try
call foo()
call print("not caught")
catch exception in e
if not (e.equals("an expected message")) then
throw exception e
end if
end try
end test
Code does not parse as Elan.
test test_try_2
try
call foo()
call print("not caught")
catch exception in e
if not (e.equals("an expected message")) then
throw exception e
end if
end try
end test
Code does not parse as Elan.
▶ To stop the program if one of two points 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 ElanRuntimeErrorexception 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 ElanRuntimeErrorexception 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 ElanRuntimeErrorexception 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 ElanRuntimeErrorexception 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 ElanRuntimeErrorexception type?(String.format("point (%) or (%) is not in the first quadrant", a, b)message?)7
}
return 0value or expression?;8
}
variable
The variable..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
while
The while condition..end while construct provides a conditional loop, used when you want execution of the enclosed instructions to begin
only if condition is true.
condition is either a Boolean variable or an expression that evaluates to a Boolean value.
When condition is true the enclosed code is executed;
when false the loop is bypassed. For example:
+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
}
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, 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.
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 or 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).
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
}
would print these lines to the display:
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? # change 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?; // change 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? ' change 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?; // change variable3
printprocedureName?(li[4]arguments?);4
printprocedureName?(li.subList(4, li.length())arguments?);5
printprocedureName?(li.subList(0, 7)arguments?);6
}
would print these lines to the display:
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 or Int arguments. The result may be a Float or an Int depending on the arguments.
For the + − * operators, the result is a Float if either of the arguments is a Float, and an Int if both arguments are Int.
For the / operator, at least one of the values must be a Float, and the result is always a Float. It can be converted to an 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
variable cname? set to sqrt(pow(a, 2) + pow(b, 2))value or expression?0
cname? = sqrt(pow(a, 2) + pow(b, 2))value or expression? # variable definition0
var cname? = sqrt(pow(a, 2) + pow(b, 2))value or expression?;0
Dim cname? = sqrt(pow(a, 2) + pow(b, 2))value or expression? ' variable definition0
var cname? = sqrt(pow(a, 2) + pow(b, 2))value or expression?;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 arguments, and the result is Int.
2/3.0 ⟶ 0.666..
2*3 ⟶ 6
2 + 3 ⟶ 5
2 − 3 ⟶ −6
11 mod 3 ⟶ 2
For integer division, either use method floor on a Float result, or use method divAsInt (which returns an 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
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 arguments and return a Boolean result.
and and or are binary operators
not is a unary operator.
The operator precedence is not → and → or, so this
example, which implements an 'exclusive or', need not use brackets and can rely on the operator precedence:
function xor(a as Boolean, b as Boolean) returns Boolean
return a and not b or b and not a
end function
Code does not parse as Elan.
function xor(a as Boolean, b as Boolean) returns Boolean
return a and not b or b and not a
end function
Code does not parse as Elan.
function xor(a as Boolean, b as Boolean) returns Boolean
return a and not b or b and not a
end function
Code does not parse as Elan.
function xor(a as Boolean, b as Boolean) returns Boolean
return a and not b or b and not a
end function
Code does not parse as Elan.
function xor(a as Boolean, b as Boolean) returns Boolean
return a and not b or b and not a
end function
Code does not parse as Elan.
String operator
The + operator is also used for concatenating String values.
Equality testing
Testing equality of Int, Float or Boolean values
Equality testing of these Value Types uses the is and isnt keywords between two values and returns a Boolean result:
- isnt returns the opposite of is.
- (a is b) returns true, if a and b are both of the same Type and their values are equal.
The only exception is that if one argument is of Type Float and the other is of Type Int,
then is will return true (and isnt will return false) if their values are the same,
i.e. they are the same whole number.
- It is also possible to use methods equals and notEqualTo described below.
Testing equality of String values and of all Reference Types
Equality testing of these Reference Types uses common dot methods equals and notEqualTo:
if listA.equals(listB) then
Code does not parse as Elan.
if listA.equals(listB) then
Code does not parse as Elan.
if listA.equals(listB) then
Code does not parse as Elan.
if listA.equals(listB) then
Code does not parse as Elan.
if listA.equals(listB) then
Code does not parse as Elan.
set differsvariableName? to set1.notEqualTo(set2)value or expression?0
differsvariableName? = set1.notEqualTo(set2)value or expression? # change variable0
differsvariableName? = set1.notEqualTo(set2)value or expression?; // change variable0
differsvariableName? = set1.notEqualTo(set2)value or expression? ' change variable0
differsvariableName? = set1.notEqualTo(set2)value or expression?; // change variable0
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, but any named value or expression that evaluates to an Int may always be used where a Float 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? # change variable0
xvariableName? = (a > b) && (b < c)value or expression?; // change variable0
xvariableName? = (a > b) And (b < c)value or expression? ' change variable0
xvariableName? = (a > b) && (b < c)value or expression?; // change variable0
set xvariableName? to (a + b) > (c - d)value or expression?0
xvariableName? = (a + b) > (c - d)value or expression? # change variable0
xvariableName? = (a + b) > (c - d)value or expression?; // change variable0
xvariableName? = (a + b) > (c - d)value or expression? ' change variable0
xvariableName? = (a + b) > (c - d)value or expression?; // change 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
- The third example above is not strictly a function call, but is a 'system method' call.
System methods may be used only within the main routine or a procedure, because they have external dependencies or side effects.
- In the fourth example, upperCase is a dot method that may be applied to any instance (variable or literal) of Type String.
See Dot methods on a String.
lambda
A lambda is a lightweight means of defining a function 'inline' and without a name (i.e. an anonymous function).
You would typically define a lambda:
- when the functionality it defines is needed in only one location:
typically for a particular call to a Higher-order Function or HoF.
- when you need to capture a local variable in the implementation: this is called 'closing around a variable'.
The syntax for a lambda is as follows:
- Start with the keyword lambda.
- Parameter definitions, comma-separated, follow the same form as parameter definitions in a function or procedure, but without surrounding brackets.
- The => symbol, which is usually articulated as 'returns', 'yields' or 'fat arrow'.
- An expression that makes use of the parameters, and may also make use of other variables that are in scope.
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:
- Similar syntax to the if instruction, but is an expression that returns a value.
- It specifies which of several expressions is to be evaluated to give the returned value.
- elif and else clauses are optional.
- You can add as many elif clauses as you wish, but only one unconditional else which, if present, must be last.
- A simpler structure that distinguishes only two possibilities, with the format:
if(Boolean expression, return value if true, return value if false).
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 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 print:
+main
1
constant 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? # constant2
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
const String 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
Const pname? = unicode(&H03c0)value or expression?2
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
final String pname? = unicode(0x03c0)value or expression?; // constant2
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
# 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 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 instruction.
Cannot use a system method in a function
A 'system method' (defined in the Standard Library) returns a value like a function does. However, because a system method
either makes changes to the system and/or depends on external inputs, it may be used only within a procedure or the main routine.
Code change required ...
Indicates that a library method or Class has been changed since the version in which your Elan code was written. The link in the
message should take you directly to information in the Library Reference documentation on how to update your code to cope with the change.
Cannot call procedure ... within an expression
A procedure may be used only within a call instruction.
Cannot invoke ... as a method
The code is attempting to use a free-standing method (function or procedure) as a 'dot method' on a named value or the result of an expression.
Cannot ...index ...
An index (in square brackets) may be applied only to certain data structure Types: String, List
and Dictionary.
Cannot range ...
A range may be applied only to certain data structure Types: String and List.
Cannot new ...
The Type specified after the call 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.
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, must specify the Type of their members, for example List<of Int>.
Failure to specify the '<of Type>' on these Types will give an error, as will specifying 'of Type' where it is not required.
Dictionaries require Types to be specified for both the keys and the values,
for example: Dictionary<of String, Float>.
May not re-assign the ...
Attempting to re-assign, or mutate, a named value that may not be re-assigned in the current context.
Name ... not unique in scope ...
Attempting to create an identifier with the same name as one already defined within the same scope.
May not set ... in a function
A property can be re-assigned only within a procedure method, not within a function, because re-assigning a property is a side effect.
The identifier ... is already used for a ... and cannot be re-defined here.
An existing named value may not be defined again within the same scope.
Duplicate Dictionary key(s)
Attempting to define a literal Dictionary
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:
- Potential keywords that might be added to Elan in future releases.
- Keywords in all the supported languages.
- Lowercase versions of Elan Type names, such as int, float, string, boolean, list, dictionary.
It is not considered good practice to use Type names for identifiers: instead you should give each identifier a name that indicates
what it does (for a method), or represents (for a named value).
- Words that are keywords in JavaScript (but not in Elan). Elan compiles to JavaScript and
use of these words as identifiers could cause errors. (If you are asking, 'Why doesn't the Elan compiler
just obfuscate these?' the answer is that we evaluated that option but concluded that it added a lot of
complexity, especially in regard to debugging and interaction with the Elan editor, just to eliminate
the minor inconvenience of having to extend the word so as to make your identifier distinct.)
For reference, the complete list of reserved words is:
action, arguments, array, async, await, boolean, break, by, byte, case, char,
const, continue, curry, debugger, default, delete, dictionary, do, double, 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, 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:
- A literal value
- A named value
- An expression
In certain very specific contexts, however, some options are disallowed by the compiler.
'computed value' field in an assert instruction
The 'actual' field should be kept as simple as possible, preferably
just a named value or a function evaluation. Generally, if you want to use a more
complex expression, it is better to evaluate it in a preceding variable
instruction and then use the named value in the 'actual' field of the assert
instruction. Some more complex expressions are permissible, but these two restrictions apply:
- Any expression involving a binary operator such as +, isnt, etc.,
must have brackets around it.
- You may not use the is operator within the 'actual' field, because
the parser will confuse this with the is keyword that is part of the assert instruction.
'variable name' field in a set instruction
The first field in a set instruction most commonly takes the name of an existing variable.
It may, however, may also take the following forms:
- Within a class it may take the form property.name to set the property name
- A Tuple deconstruction. See Tuple
- A list deconstruction. See List
literal value or data structure in a constant
The value of a constant must be a literal value of a Type that is not mutable.
This can be a simple value (e.g. a number or string), or an immutable List or Dictionary.
'values' field in an enum definition
enum values must each be a valid identifier, separated by commas.
'message' field in a throw instruction
An exception message must be either a literal string or a named value holding a string.
expression field - used within multiple instructions
This field expects an expression. For the various forms of expression see Expressions.
identifier field - used within multiple instructions
'if' field in an else clause
'inherits ClassName' field in a class
An inheritance clause, if used, must consist of the keyword inherits
followed by a space and then one or more Type names separated by commas.
'name' field in a function or procedure definition
A method name must follow the rules for an identifier.
'parameter definitions' in a function or procedure definition
Each parameter definition takes the form:
name as Type
The name must follow the rules for an identifier.
The Type must follow the rules for a Type.
If more than one parameter is defined, the definitions must be separated by commas.
'procedureName' in a call statement
Valid forms for a procedure call are
- call procedureName()
- call instanceName.procedureMethodName()
- call property.propertyName.procedureMethodName()
- call library.procedureName()
The last one is used only if there is a need to disambiguate between a library procedure and a user-defined (global)
procedure with the same name.
'Type' field in a function or property definition
For certain Types the name may be followed by an of clause, for example:
List<
of Int>
Dictionary<
of String,
Int>
'Name' field in a class or enum definition
Type names always begin with a capital letter, optionally followed by letters of either case, numeric digits,
or underscore symbols. Nothing else.
'name' field in a let or variable instruction
The definition for a variable instruction is most commonly
a simple name. Less commonly, it may take the form of a dconstruction of a
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 instructions; otherwise it might become impossible to add more text.
ReferenceError: Cannot access '[name]' before initialisation
Elan Language Reference go to the top