Procedural programming Reference
Getting started
Select the procedural programming profile
To undertake procedural programming (PP) in Elan, ensure that Elan is set to the Procedural profile from the menu at the top of the IDE.
This will ensure that:
- The list of demo programs will now show only programs that use PP.
- The options shown on the new code menus in the editor are all appropriate to PP. These are listed below under
Global Instructions and Statement Instructions.
Why coding with Elan is different
To start programming with Elan, first familiarise yourself using the Elan IDE, and especially with
entering and editing code, by watching the videos in this document [LINK REQUIRED].
You will learn, therein, that Elan supports programming in Python, VB, C#, or Java (as well as the Reference Language),
and offers the option of immediate translation between all four languages - either way - if desired.
However, if you have previously programmed in any of those languages you will finds that Elan enforces
a few constraints on how code is written. There are three forms of these constraints:
- Enforced best practices in coding
- Use of a common library across all the supported languages.
- Some, generally smaller, constraints required by the need for portability across languages.
These are described in more detail below
Enforced best practices in coding
Elan supports, and in many cases enforces a set of good programming practices.
This applies across all the supported languages - even where
a language, if used outside Elan, would allow you to do otherwise. In the context of procedural programming
- where the procedural profile has been selected in the IDE, there are seven such best practices.
Some of these best practices are widely recognised, others less so.
Some programmers will argue that there are very specific circumstances where
breaking the rules should be acceptable. The problem with this line of argument is that most of the places where
even professional programmers break the rules don't fit those circumstances - they are broken because the programmer just finds
it more convenient. It is far better that you learn to program without breaking these rules than that you adopt
bad habits from the beginning. This will result in you writing better code, and encountering fewer unexpected problems.
The seven best practices enforced by Elan - for procedural programming, this is -are:
[INSERT TOC of the h3 headings in this section]
No global variables
It is widely recognised that global variables are a bad idea - encouraging poor program structure and leading to unexpected
behaviour. Yet most programming languages permit them. The Elan editor solves this by offering the variable definition
instruction only within the main routine, functions, or procedures. At global level the only permitted definition of a named value
is a constant, and that, too, is constrained to be of a type that cannot be mutated by any instruction in the program.
In Java, all code must be within a class. Elan provides a boilerplate class named Global
Code does not parse as Elan.Global
Code does not parse as Elan.Global
Code does not parse as Elan.Global
Code does not parse as Elan.Global
Code does not parse as Elan. to adhere to this rule,
and all code must be added within this, and everything added immediately within the Global
Code does not parse as Elan.Global
Code does not parse as Elan.Global
Code does not parse as Elan.Global
Code does not parse as Elan.Global
Code does not parse as Elan. is deemed, in Elan terms, to be
'global code', so you will not be able to add a variable - even in the form of a 'property' there.
Static typing
Static typing means that the type of any variable, or parameter, or value returned by a function, is known at compile time,
and that type never changes, even though the value may change. VB, C#, and Java, are inherently statically-typed. Python is inherently dynamically-typed
but since Python 3.5 has offered the option to add 'type hints' which allow working in a statically typed manner. While some programmers like
the idea of dynamic typing, there is almost no need for it in most forms of programming, and it is well established that
dynamic typing does not work well when building large, complex systems. Moreover, in a pure educational context, using static typing gives a stronger
understanding that, in all modern languages, statically- or dynamically-typed, all data elements have an associated type - and static typing helps
you to understand the nature of those types. When entering/editing Python, Elan therefore enforces the use of type hints where needed.
Elan requires types to be specified for all parameters for procedures or functions, and for the return type of a function.
Elan does not, however, require the type to be specified explicitly for any variable (or constant) because the type is inferred from the
initial value given to it.
No null references
All programmers have encourntered the notorious 'Null Reference ...` (or 'Null Pointer ...`) error message, and these can sometimes be
very hard to track down. Tony Hoare, Turing Medal recipient and one of Britain's greatest computer scientists, magnanimously described his invention
of 'null' as a Billion Dollar Mistake.
All the supported programming languages inherently permit null reference errors - but when you are coding in Elan it is impossible for one
to arise because:
- Every variable definition must specify an initial value.
- It may never be reassigned to null.
- (Additional, related rules are enforced in object-oriented programming.)
No breaking out of loops before they have completed
Python, C#, and Java all have a break instruction (the VB equivalent is exit) which allow you to break out of
a loop. Elan does not permit any of those instructions to be used. Why?
The break instruction (or equivalent) was invented when the only possible kind of loops that programming languages
supported executed a specified number of times - there were no 'conditional loops'. With the advent of 'structured programming'
came proper conditional loops, of which the most common form is the 'while loop'. After structured programming it is never necessary
to break out from a loop before its natural termination.
Return from a function or procedure only after the final instruction
This principle is somewhat analogous to the previous one - where programmers find it 'convenient' to include multiple
returnreturnreturnreturnreturn statements within a procedure or a function. All the supported languages permit this in most
other environments. Elan, however, deliberately prevents this practice. In Elan a function always has a return statement as its last
instruction (which specifies the value to be returned), and there is no option to add another one earlier.
And in Elan a procedure never has a return instruction - the procedure exits after the last instruction.
But where is the harm in 'early returns'? Here are four answers:
- You cannot jump into the middle of a procedure or function, nor should be able to jump out of it in the middle
- They lead to poorly-structured code
- They make it harder to debug code - because you may need to find, and put a separate break-points on each return instruction.
- When you change the requirements of the function - for example that the returned string is always
uppercase - you need to enforce this in several places, and you run the risk of overlooking one them.
(Some languages offer a 'finally' clause to make that easier - but this is really just a kludge to paper over
the cracks of poor structure.)
Some programmers argue that the constraint of no early returns in a function, means you have to write an extra line of code at the start
to define a variable that holds the result. True - but that's a small price to pay, and it also makes your code more readable.
All functions must be pure
Many textbooks draw no distinction between procedures and functions, except that the latter return a value to the calling code.
Used properly, the two are distinct in another, very important, respect.
Understanding procedures
A procedure:
- can be thought of as a way of adding you own custom instruction to the language
- makes a change to the state of the system: which might be a result printed on the screen or saved to a file,
or maybe changing the data it was given, such as sorting a list (for example, the 'in place' Ripple Sort demo)
- may depend on the system in others ways, for example requesting a response from the user
- should be called in isolation, never within an expression.
By contrast, a properfunction:
- can be thought of as a way of adding a custom operator to the language - a way to derive a new value from values provided.
- does not make any change to the state of the system. The only observable effect of calling a function is the returned
value, which is held, temporarily, only on the stack, to be consumed by something else. Another way to say this is that a function creates no side-effects,
- may not depend on the system. The value returned must depend solely, and deterministically, on the values provided as parameters.
- should always be called within an expression (or as the complete expression), never in isolation - also known as a 'hanging function call'.
Although some programming languages make a syntactic distinction between a procedure and a function (some don't), the
only ones that enforce the distinctions made above are the 'pure' functional programming languages, such as Haskell, OCAML, F# ...
Elan ensures that all functions are proper functions, but places no restrictions on what you can do in a procedure (except return a value).
In the long term, if you wish to progress further with programming, laying this foundation will make it far easier for you to
transition to 'functional programming' than otherwise. But making this distinction from the outset offers more immediate benefits:
- Fewer bugs - specifically those arising from the unforseen clashes between the side-effects of procedures.
- Easier to 'reason about' the correctness of the program, because functions then behave like mathematical functions.
Specifically if function foo is correct, and function bar is correct, then
combining them in any way is also correct code. That cannot be inferred from combining two procedures.
- Functions can be more easily tested, which leads us on to our last best practice, here ...
Every function should be unit tested
Historically, most testing of programs was done by humans, running through a large number of 'test scripts' (or 'cases') manually to check that
the delivered system worked correctly, then again after every single change or extension to the system, to ensure that
existing functionality had not been broken accidentally.
In recent years, the emphasis as shifted to automated testing. Although writing the test scripts/cases still takes, once written, all the
tests can be run repeatedly at the touch of a button, and even triggered automatically by any change to the code.
There are many forms of automated testing, but 'unit testing' is the most popular. All modern languages offer a 'unit testing framework',
but they vary considerably in how easy they are to write or run.
Unit tests are intended for testing proper functions though many do not enforce that. But running unit tests on procedures,
or improper functions (that create side effects) is dangerous - running the tests might result in data being written to a file or database,
or a network message being sent repeatedly, for example. There are software frameworks for automated tesing of procedures and even whole applications
(known as 'end to end testing') - but these frameworks are much harder to use.
Many professional software development teams enforce the writing of unit tests for every function, but this is typically something
that most programmers don't discover unless they turn professional. But it is a practice that even complete beginners would benefit from:
- It gives you confidence that code is correct, especialy code that you haven't looked at recently.
- It gives you an immediate sense of reward when all the tests 'go green' (pass).
- It gives you confidence to improve existing code (also known as 'refactoring') without the fear of unknowingly breaking it in subtle ways.
Elan provides unit testing, for all the supported languages, that is even simpler to use (both for writing the tests and running them) than most
other tools. Although it does not enforce the rule that every function written should have automated unit tests, it does make it
so easy, that it hopefully encourages learners to use the practice
Use of a common library across all the supported languages.
All programming languages have their own standard libraries for:
- mathematical and scientific functions
- manipulating strings, lists, and other data structures
- textual input/output
- graphics
and more. There are many similarities between the libraries of different languages, but also significant differences. Elan defines its own library
which offers the identical functions in each of the supported languages. The Elan library has been designed to find as much common ground as possible but,
inevitably, it is not identical to the standard library for any of the supported languages.
One consequence of this is that even though the Python, C#, VB, or Java code generated within Elan is always syntactically valid for the language,
you won't typically be able to run exported code in another environment without editing the library calls. (We hope to provide automated, or
semi-automated, tools for this in future.)
Other constraints
Elan enforces a few other constraints that aren't driven by best practices, or by the common library, but because of small arbitrary differences between
the target languages. Fortunately these are quite small both in number and impact. There is no need to learn them explicitly, because in all cases
where you might encounter one, you will get a clear error message that gives you the solution. But here is a list of the most important ones:
- Elan is case-sensitive for identifiers, but you deliberately cannot define two identifiers in the same scope that differ only by case.
- All languages disallow the use of certain reserved words as identifiers. Elan deliberately disallows the reserved words from all the supported languages
- so that translations are guaranteed to run.
- Literal strings must be bounded by double-quotes, not by single-quotes
- Some languages distinguish between a 'for loop' and a 'for each loop'. Elan, like Python, uses the same syntax for both.
When you for loop in Elan the syntax in VB, C#, and Java will be that of a 'for each' loop.
- Raising a number to a power must be done with the pow function: the 'power operator' symbols will not be recognised in the
languages that have them.
- Individual values are extracted from a tuple using the syntax
myTuple.item_0myTuple.item_0myTuple.item_0myTuple.item_0myTuple.item_0, myTuple.item_1myTuple.item_1myTuple.item_1myTuple.item_1myTuple.item_1 etc.
Dedicated 'Tuple deconstruction' syntax is not recognised for any of the languages.
- Equality testing of value types uses the equality operator for each language e.g.
a is ba == ba == ba = ba == b, but if the types
are reference types (e.g. lists or strings) you must use the syntax a.equals(b)a.equals(b)a.equals(b)a.equals(b)a.equals(b)
- Division of two floating point numbers (or one floating point and one integer) may use the division operator - e.g.
a/ba/ba/ba/ba/b - and
the result will always be floating point.
But if both values are integer type then you must use either divAsInt(a, b)divAsInt(a, b)divAsInt(a, b)divAsInt(a, b)divAsInt(a, b) or divAsInt(a, b)divAsInt(a, b)divAsInt(a, b)divAsInt(a, b)divAsInt(a, b)
to make clear which result you are looking for (divAsInt will round down the result if necessary).
- 2-dimension arrays are implemented as a list of lists. However,
- Negative index values, and index ranges are not supported, but you can use subString or subString to achieve the same effect.
- Passing by reference, if wanted, is done by, for example, wrapping a parameter of type
IntintintIntegerint as a Ref
Code does not parse as Elan.Ref
Code does not parse as Elan.Ref
Code does not parse as Elan.Ref
Code does not parse as Elan.Ref
Code does not parse as Elan..
Global instructions
Global instructions are placed directly in your code file.
They are never indented from the left-hand edge, nor may they be located within other instructions.
Main routine
Examples of main routines
p>From the
Burrow demo. For very simple applications like this, the main routine is the whole program:
+main
1
variable blocksname? set to createBlockGraphics(white)value or expression?2
variable xname? set to 20value or expression?3
variable yname? set to 15value or expression?4
+while truecondition?
5
reassign blocks[x][y]variableName? to redvalue or expression?6
call displayBlocksprocedureName?(blocksarguments?)7
reassign blocks[x][y]variableName? to blackvalue or expression?8
variable directionname? set to randint(0, 3)value or expression?9
+if direction is 0condition? then
10
reassign xvariableName? to min([x + 1, 39])value or expression?11
elif direction is 1condition? then12
reassign xvariableName? to max([x - 1, 0])value or expression?13
elif direction is 2condition? then14
reassign yvariableName? to min([y + 1, 29])value or expression?15
elif direction is 3condition? then16
reassign yvariableName? to max([y - 1, 0])value or expression?17
end if
end while
end main
+def main() -> None:
1
blocksname? = createBlockGraphics(white)value or expression? # variable definition2
xname? = 20value or expression? # variable definition3
yname? = 15value or expression? # variable definition4
+while Truecondition?:
5
blocks[x][y]variableName? = redvalue or expression? # reassign variable6
displayBlocksprocedureName?(blocksarguments?) # call procedure7
blocks[x][y]variableName? = blackvalue or expression? # reassign variable8
directionname? = randint(0, 3)value or expression? # variable definition9
+if direction == 0condition?:
10
xvariableName? = min([x + 1, 39])value or expression? # reassign variable11
elif direction == 1condition?: # else if12
xvariableName? = max([x - 1, 0])value or expression? # reassign variable13
elif direction == 2condition?: # else if14
yvariableName? = min([y + 1, 29])value or expression? # reassign variable15
elif direction == 3condition?: # else if16
yvariableName? = max([y - 1, 0])value or expression? # reassign variable17
main()
+static void main() {
1
var blocksname? = createBlockGraphics(white)value or expression?;2
var xname? = 20value or expression?;3
var yname? = 15value or expression?;4
+while (truecondition?) {
5
blocks[x][y]variableName? = redvalue or expression?; // reassign variable6
displayBlocksprocedureName?(blocksarguments?); // call procedure7
blocks[x][y]variableName? = blackvalue or expression?; // reassign variable8
var directionname? = randint(0, 3)value or expression?;9
+if (direction == 0condition?) {
10
xvariableName? = min([x + 1, 39])value or expression?; // reassign variable11
} else if (direction == 1condition?) {12
xvariableName? = max([x - 1, 0])value or expression?; // reassign variable13
} else if (direction == 2condition?) {14
yvariableName? = min([y + 1, 29])value or expression?; // reassign variable15
} else if (direction == 3condition?) {16
yvariableName? = max([y - 1, 0])value or expression?; // reassign variable17
} // if
} // while
} // main
+Sub main()
1
Dim blocksname? = createBlockGraphics(white)value or expression? ' variable definition2
Dim xname? = 20value or expression? ' variable definition3
Dim yname? = 15value or expression? ' variable definition4
+While Truecondition?
5
blocks[x][y]variableName? = redvalue or expression? ' reassign variable6
displayBlocksprocedureName?(blocksarguments?) ' call procedure7
blocks[x][y]variableName? = blackvalue or expression? ' reassign variable8
Dim directionname? = randint(0, 3)value or expression? ' variable definition9
+If direction = 0condition? Then
10
xvariableName? = min({x + 1, 39})value or expression? ' reassign variable11
ElseIf direction = 1condition? Then12
xvariableName? = max({x - 1, 0})value or expression? ' reassign variable13
ElseIf direction = 2condition? Then14
yvariableName? = min({y + 1, 29})value or expression? ' reassign variable15
ElseIf direction = 3condition? Then16
yvariableName? = max({y - 1, 0})value or expression? ' reassign variable17
End If
End While
End Sub
public class Global {
+static void main() {
1
var blocksname? = createBlockGraphics(white)value or expression?;2
var xname? = 20value or expression?;3
var yname? = 15value or expression?;4
+while (truecondition?) {
5
blocks[x][y]variableName? = redvalue or expression?; // reassign variable6
displayBlocksprocedureName?(blocksarguments?); // call procedure7
blocks[x][y]variableName? = blackvalue or expression?; // reassign variable8
var directionname? = randint(0, 3)value or expression?;9
+if (direction == 0condition?) {
10
xvariableName? = min([x + 1, 39])value or expression?; // reassign variable11
} else if (direction == 1condition?) {12
xvariableName? = max([x - 1, 0])value or expression?; // reassign variable13
} else if (direction == 2condition?) {14
yvariableName? = min([y + 1, 29])value or expression?; // reassign variable15
} else if (direction == 3condition?) {16
yvariableName? = max([y - 1, 0])value or expression?; // reassign variable17
} // if
} // while
} // main
}
From the Ripple Sort demo. The main routine defines the list to be sorted,
delegates most of the work to the inPlaceRippleSort procedure (not shown here),
then prints the sorted list:
+main
1
variable liname? set to [3, 6, 1, 0, 99, 4, 67]value or expression?2
call inPlaceRippleSortprocedureName?(liarguments?)3
print(liarguments?)4
end main
+def main() -> None:
1
liname? = [3, 6, 1, 0, 99, 4, 67]value or expression? # variable definition2
inPlaceRippleSortprocedureName?(liarguments?) # call procedure3
print(liarguments?)4
main()
+static void main() {
1
var liname? = [3, 6, 1, 0, 99, 4, 67]value or expression?;2
inPlaceRippleSortprocedureName?(liarguments?); // call procedure3
Console.WriteLine(liarguments?); // print4
} // main
+Sub main()
1
Dim liname? = {3, 6, 1, 0, 99, 4, 67}value or expression? ' variable definition2
inPlaceRippleSortprocedureName?(liarguments?) ' call procedure3
Console.WriteLine(liarguments?) ' print4
End Sub
public class Global {
+static void main() {
1
var liname? = [3, 6, 1, 0, 99, 4, 67]value or expression?;2
inPlaceRippleSortprocedureName?(liarguments?); // call procedure3
System.out.println(liarguments?); // print4
} // main
}
From the Life procedural demo. The whole program is 157 instructions (including tests), but the main routine contains just 9.
It is good practice to keep the main routine small.
+main
1
variable gridname? set to createBlockGraphics(white)value or expression?2
call fillRandomprocedureName?(gridarguments?)3
+while truecondition?
4
call displayBlocksprocedureName?(gridarguments?)5
variable gridRefname? set to new AsRef<of List<of List<of Int>>>(grid)value or expression?6
call nextGenerationprocedureName?(gridRefarguments?)7
reassign gridvariableName? to gridRef.value()value or expression?8
call sleep_msprocedureName?(50arguments?)9
end while
end main
+def main() -> None:
1
gridname? = createBlockGraphics(white)value or expression? # variable definition2
fillRandomprocedureName?(gridarguments?) # call procedure3
+while Truecondition?:
4
displayBlocksprocedureName?(gridarguments?) # call procedure5
gridRefname? = AsRef[list[list[int]]](grid)value or expression? # variable definition6
nextGenerationprocedureName?(gridRefarguments?) # call procedure7
gridvariableName? = gridRef.value()value or expression? # reassign variable8
sleep_msprocedureName?(50arguments?) # call procedure9
main()
+static void main() {
1
var gridname? = createBlockGraphics(white)value or expression?;2
fillRandomprocedureName?(gridarguments?); // call procedure3
+while (truecondition?) {
4
displayBlocksprocedureName?(gridarguments?); // call procedure5
var gridRefname? = new AsRef<List<List<int>>>(grid)value or expression?;6
nextGenerationprocedureName?(gridRefarguments?); // call procedure7
gridvariableName? = gridRef.value()value or expression?; // reassign variable8
sleep_msprocedureName?(50arguments?); // call procedure9
} // while
} // main
+Sub main()
1
Dim gridname? = createBlockGraphics(white)value or expression? ' variable definition2
fillRandomprocedureName?(gridarguments?) ' call procedure3
+While Truecondition?
4
displayBlocksprocedureName?(gridarguments?) ' call procedure5
Dim gridRefname? = New AsRef(Of List(Of List(Of Integer)))(grid)value or expression? ' variable definition6
nextGenerationprocedureName?(gridRefarguments?) ' call procedure7
gridvariableName? = gridRef.value()value or expression? ' reassign variable8
sleep_msprocedureName?(50arguments?) ' call procedure9
End While
End Sub
public class Global {
+static void main() {
1
var gridname? = createBlockGraphics(white)value or expression?;2
fillRandomprocedureName?(gridarguments?); // call procedure3
+while (truecondition?) {
4
displayBlocksprocedureName?(gridarguments?); // call procedure5
var gridRefname? = new AsRef<List<List<int>>>(grid)value or expression?;6
nextGenerationprocedureName?(gridRefarguments?); // call procedure7
gridvariableName? = gridRef.value()value or expression?; // reassign variable8
sleep_msprocedureName?(50arguments?); // call procedure9
} // while
} // main
}
What you need to know about the main routine
- The first instruction added within the main routine, is always the first instruction to be executed when the program is run.
- Without a main routine, there is nothing to run. However you may write and test functions without a main.
- The main routine does not have to be at the top of the file, but this is a good convention to follow.
- You may not add another main routine within a program, unless you first ghost the existing one.
- The main routine may form the entire program (as in the Burrowexample above); more commonly it will delegate
to procedures and functions defined by the programmer.
Procedure
Examples of procedures
From the Bubbles demo. It changes the vertical position, and the size, of each bubble in the provided list:
+procedure moveGrowBurstname?(bubbles as List<of CircleVG>parameter definitions?)
1
+for bitem? in bubblessource?
2
+if random() < 0.05condition? then
3
#
call b.setRadiusprocedureName?(0arguments?)4
call b.setCentreYprocedureName?(75arguments?)5
else6
#
call b.setCentreYprocedureName?(b.centreY - 1arguments?)7
call b.setRadiusprocedureName?(b.radius + 0.2arguments?)8
end if
end for
call displayVectorGraphicsprocedureName?(bubblesarguments?)9
call sleep_msprocedureName?(5arguments?)10
end procedure
+def moveGrowBurstname?(bubbles: list[CircleVG]parameter definitions?) -> None:
# procedure1
+for bitem? in bubblessource?:
2
+if random() < 0.05condition?:
3
#
b.setRadiusprocedureName?(0arguments?) # call procedure4
b.setCentreYprocedureName?(75arguments?) # call procedure5
else:6
#
b.setCentreYprocedureName?(b.centreY - 1arguments?) # call procedure7
b.setRadiusprocedureName?(b.radius + 0.2arguments?) # call procedure8
displayVectorGraphicsprocedureName?(bubblesarguments?) # call procedure9
sleep_msprocedureName?(5arguments?) # call procedure10
+static void moveGrowBurstname?(List<CircleVG> bubblesparameter definitions?) {
// procedure1
+foreach (bitem? in bubblessource?) {
2
+if (random() < 0.05condition?) {
3
//
b.setRadiusprocedureName?(0arguments?); // call procedure4
b.setCentreYprocedureName?(75arguments?); // call procedure5
} else {6
//
b.setCentreYprocedureName?(b.centreY - 1arguments?); // call procedure7
b.setRadiusprocedureName?(b.radius + 0.2arguments?); // call procedure8
} // if
} // foreach
displayVectorGraphicsprocedureName?(bubblesarguments?); // call procedure9
sleep_msprocedureName?(5arguments?); // call procedure10
} // procedure
+Sub moveGrowBurstname?(bubbles As List(Of CircleVG)parameter definitions?)
' procedure1
+For Each bitem? In bubblessource?
2
+If random() < 0.05condition? Then
3
'
b.setRadiusprocedureName?(0arguments?) ' call procedure4
b.setCentreYprocedureName?(75arguments?) ' call procedure5
Else6
'
b.setCentreYprocedureName?(b.centreY - 1arguments?) ' call procedure7
b.setRadiusprocedureName?(b.radius + 0.2arguments?) ' call procedure8
End If
Next b
displayVectorGraphicsprocedureName?(bubblesarguments?) ' call procedure9
sleep_msprocedureName?(5arguments?) ' call procedure10
End Sub
public class Global {
+static void moveGrowBurstname?(List<CircleVG> bubblesparameter definitions?) {
// procedure1
+foreach (bitem? in bubblessource?) {
2
+if (random() < 0.05condition?) {
3
//
b.setRadiusprocedureName?(0arguments?); // call procedure4
b.setCentreYprocedureName?(75arguments?); // call procedure5
} else {6
//
b.setCentreYprocedureName?(b.centreY - 1arguments?); // call procedure7
b.setRadiusprocedureName?(b.radius + 0.2arguments?); // call procedure8
} // if
} // foreach
displayVectorGraphicsprocedureName?(bubblesarguments?); // call procedure9
sleep_msprocedureName?(5arguments?); // call procedure10
} // procedure
}
It is called within the main routine by this instruction: call moveGrowBurstprocedureName?(bubblesarguments?)0moveGrowBurstprocedureName?(bubblesarguments?) # call procedure0moveGrowBurstprocedureName?(bubblesarguments?); // call procedure0moveGrowBurstprocedureName?(bubblesarguments?) ' call procedure0moveGrowBurstprocedureName?(bubblesarguments?); // call procedure0
From the Turtle Dragon demo. The procedure defines two parameters of different types. The body of this procedure consists
of a straight sequence of call procedure instructions, but all the procedures are defined on the turtle (the parameter named ttttt)
using 'dot-syntax':
+procedure setupTurtlename?(t as Turtle, order as Intparameter definitions?)
1
call t.turnToHeadingprocedureName?(180 + order*45arguments?)2
call t.placeAtprocedureName?(-40, 20arguments?)3
call t.penColourprocedureName?(redarguments?)4
call t.penWidthprocedureName?(10.0/orderarguments?)5
call t.penDownprocedureName?(arguments?)6
call t.showprocedureName?(arguments?)7
end procedure
+def setupTurtlename?(t: Turtle, order: intparameter definitions?) -> None:
# procedure1
t.turnToHeadingprocedureName?(180 + order*45arguments?) # call procedure2
t.placeAtprocedureName?(-40, 20arguments?) # call procedure3
t.penColourprocedureName?(redarguments?) # call procedure4
t.penWidthprocedureName?(10.0/orderarguments?) # call procedure5
t.penDownprocedureName?(arguments?) # call procedure6
t.showprocedureName?(arguments?) # call procedure7
+static void setupTurtlename?(Turtle t, int orderparameter definitions?) {
// procedure1
t.turnToHeadingprocedureName?(180 + order*45arguments?); // call procedure2
t.placeAtprocedureName?(-40, 20arguments?); // call procedure3
t.penColourprocedureName?(redarguments?); // call procedure4
t.penWidthprocedureName?(10.0/orderarguments?); // call procedure5
t.penDownprocedureName?(arguments?); // call procedure6
t.showprocedureName?(arguments?); // call procedure7
} // procedure
+Sub setupTurtlename?(t As Turtle, order As Integerparameter definitions?)
' procedure1
t.turnToHeadingprocedureName?(180 + order*45arguments?) ' call procedure2
t.placeAtprocedureName?(-40, 20arguments?) ' call procedure3
t.penColourprocedureName?(redarguments?) ' call procedure4
t.penWidthprocedureName?(10.0/orderarguments?) ' call procedure5
t.penDownprocedureName?(arguments?) ' call procedure6
t.showprocedureName?(arguments?) ' call procedure7
End Sub
public class Global {
+static void setupTurtlename?(Turtle t, int orderparameter definitions?) {
// procedure1
t.turnToHeadingprocedureName?(180 + order*45arguments?); // call procedure2
t.placeAtprocedureName?(-40, 20arguments?); // call procedure3
t.penColourprocedureName?(redarguments?); // call procedure4
t.penWidthprocedureName?(10.0/orderarguments?); // call procedure5
t.penDownprocedureName?(arguments?); // call procedure6
t.showprocedureName?(arguments?); // call procedure7
} // procedure
}
From the Life - procedural demo. The code sets each of the cells in the 40x30 gridgridgridgridgrid
+procedure fillRandomname?(grid as List<of List<of Int>>parameter definitions?)
1
+for colitem? in range(0, 40)source?
2
+for rowitem? in range(0, 30)source?
3
reassign grid[col][row]variableName? to blackOrWhite(random())value or expression?4
end for
end for
end procedure
+def fillRandomname?(grid: list[list[int]]parameter definitions?) -> None:
# procedure1
+for colitem? in range(0, 40)source?:
2
+for rowitem? in range(0, 30)source?:
3
grid[col][row]variableName? = blackOrWhite(random())value or expression? # reassign variable4
+static void fillRandomname?(List<List<int>> gridparameter definitions?) {
// procedure1
+foreach (colitem? in range(0, 40)source?) {
2
+foreach (rowitem? in range(0, 30)source?) {
3
grid[col][row]variableName? = blackOrWhite(random())value or expression?; // reassign variable4
} // foreach
} // foreach
} // procedure
+Sub fillRandomname?(grid As List(Of List(Of Integer))parameter definitions?)
' procedure1
+For Each colitem? In range(0, 40)source?
2
+For Each rowitem? In range(0, 30)source?
3
grid[col][row]variableName? = blackOrWhite(random())value or expression? ' reassign variable4
Next row
Next col
End Sub
public class Global {
+static void fillRandomname?(List<List<int>> gridparameter definitions?) {
// procedure1
+foreach (colitem? in range(0, 40)source?) {
2
+foreach (rowitem? in range(0, 30)source?) {
3
grid[col][row]variableName? = blackOrWhite(random())value or expression?; // reassign variable4
} // foreach
} // foreach
} // procedure
}
What you need to know about procedures
- A procedure (also known as a 'subroutine') defines a part of the program to which work
is delegated, either by the main routine, or by another procedure.
- The Elan library provides some ready-made procedures such as print and pause.
- The procedure instruction allows you to define your own procedures.
- A procedure is defined with a unique name, starting lower-case (moveGrowBurst).
- It may optionally define one or more parameters (one in the example above), comma-separated if more than one.
- Each parameter specifies a name, starting lower-case (
bubblesbubblesbubblesbubblesbubbles above) and a type (List<of CircleVG>list[CircleVG]List<CircleVG>List(Of CircleVG)List<CircleVG> above) using the syntax
bubbles as List
Code does not parse as Elan.bubbles as List
Code does not parse as Elan.bubbles as List
Code does not parse as Elan.bubbles as List
Code does not parse as Elan.bubbles as List
Code does not parse as Elan.
- The procedure is called from elsewhere using a call procedure instruction.
which specifies the name of the procedure to be called, and includes a value(s) or expressions(s) that match the type(s) for the parameter(s).
- The procedure always finishes on the last instruction defined within it, at which point the program continues from where the call instruction
was located.
- Changes made to parameter values (where possible) within the procedure (such as changing the radius and centre of each bubble
in the list
new codenew codenew codenew codepublic class Global {
new code
}
which specifies the name of the procedure to be called, and includes a value(s) or expressions(s) that match the type(s) for the parameter(s).
- The procedure always finishes on the last instruction defined within it, at which point the program continues from where the call instruction
was located.
- Changes made to parameter values (where possible) within the procedure (such as changing the radius and centre of each bubble
in the list
bubblesbubblesbubblesbubblesbubbles) will be visible to the calling code when the procedure exits.
- Functions may be called from within a procedure, but not vice versa.
- Instructions within a procedure - unlike within a function - may call other procedures, undertake input/output, and use system methods.
More advanced techniques
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 AsRefAsRefAsRefAsRefAsRef.
This then allows use of the dot methods value and set on it to read and change its value,
as in this example where 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
print(argRef.value()arguments?)5
end main
+procedure changeArgname?(pointer as AsRef<of String>parameter definitions?)
6
variable rname? set to pointer.value()value or expression?7
call pointer.setprocedureName?(r.upperCase()arguments?)8
end procedure
+def main() -> None:
1
argname? = "abc"value or expression? # variable definition2
argRefname? = AsRef[str](arg)value or expression? # variable definition3
changeArgprocedureName?(argRefarguments?) # call procedure4
print(argRef.value()arguments?)5
+def changeArgname?(pointer: AsRef[str]parameter definitions?) -> None:
# procedure6
rname? = pointer.value()value or expression? # variable definition7
pointer.setprocedureName?(r.upperCase()arguments?) # call procedure8
main()
+static void main() {
1
var argname? = "abc"value or expression?;2
var argRefname? = new AsRef<string>(arg)value or expression?;3
changeArgprocedureName?(argRefarguments?); // call procedure4
Console.WriteLine(argRef.value()arguments?); // print5
} // main
+static void changeArgname?(AsRef<string> pointerparameter definitions?) {
// procedure6
var rname? = pointer.value()value or expression?;7
pointer.setprocedureName?(r.upperCase()arguments?); // call procedure8
} // procedure
+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
Console.WriteLine(argRef.value()arguments?) ' print5
End Sub
+Sub changeArgname?(pointer As AsRef(Of String)parameter definitions?)
' procedure6
Dim rname? = pointer.value()value or expression? ' variable definition7
pointer.setprocedureName?(r.upperCase()arguments?) ' call procedure8
End Sub
public class Global {
+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
System.out.println(argRef.value()arguments?); // print5
} // main
+static void changeArgname?(AsRef<String> pointerparameter definitions?) {
// procedure6
var rname? = pointer.value()value or expression?;7
pointer.setprocedureName?(r.upperCase()arguments?); // call procedure8
} // procedure
}
Recursive procedure calls
A procedure may call itself, directly or indirectly (calling another procedure that then calls back to the first one).
The following example is from the Turtle Snowflake demo, you can see that the drawSide
procedure calls itself from four separate places within its body - in order to achieve the 'fractal' effect, where
a basic shape repeats itself at different levels of scale. You can also observe two very important principles of
recursion here:
- That calls back into itself are with a smaller version of the problem - in this case a
thirdthirdthirdthirdthird of the lengthlengthlengthlengthlength
that the procedure was given.
- That there is a terminating condition where the recursion 'bottoms out' - in this when the side length gets down to 1.
+procedure drawSidename?(length as Float, t as Turtleparameter definitions?)
1
+if (length > 1)condition? then
2
variable thirdname? set to length/3value or expression?3
call drawSideprocedureName?(third, targuments?)4
call t.turnprocedureName?(-60arguments?)5
call drawSideprocedureName?(third, targuments?)6
call t.turnprocedureName?(120arguments?)7
call drawSideprocedureName?(third, targuments?)8
call t.turnprocedureName?(-60arguments?)9
call drawSideprocedureName?(third, targuments?)10
else11
call t.moveprocedureName?(lengtharguments?)12
end if
end procedure
+def drawSidename?(length: float, t: Turtleparameter definitions?) -> None:
# procedure1
+if (length > 1)condition?:
2
thirdname? = length/3value or expression? # variable definition3
drawSideprocedureName?(third, targuments?) # call procedure4
t.turnprocedureName?(-60arguments?) # call procedure5
drawSideprocedureName?(third, targuments?) # call procedure6
t.turnprocedureName?(120arguments?) # call procedure7
drawSideprocedureName?(third, targuments?) # call procedure8
t.turnprocedureName?(-60arguments?) # call procedure9
drawSideprocedureName?(third, targuments?) # call procedure10
else:11
t.moveprocedureName?(lengtharguments?) # call procedure12
+static void drawSidename?(double length, Turtle tparameter definitions?) {
// procedure1
+if ((length > 1)condition?) {
2
var thirdname? = length/3value or expression?;3
drawSideprocedureName?(third, targuments?); // call procedure4
t.turnprocedureName?(-60arguments?); // call procedure5
drawSideprocedureName?(third, targuments?); // call procedure6
t.turnprocedureName?(120arguments?); // call procedure7
drawSideprocedureName?(third, targuments?); // call procedure8
t.turnprocedureName?(-60arguments?); // call procedure9
drawSideprocedureName?(third, targuments?); // call procedure10
} else {11
t.moveprocedureName?(lengtharguments?); // call procedure12
} // if
} // procedure
+Sub drawSidename?(length As Double, t As Turtleparameter definitions?)
' procedure1
+If (length > 1)condition? Then
2
Dim thirdname? = length/3value or expression? ' variable definition3
drawSideprocedureName?(third, targuments?) ' call procedure4
t.turnprocedureName?(-60arguments?) ' call procedure5
drawSideprocedureName?(third, targuments?) ' call procedure6
t.turnprocedureName?(120arguments?) ' call procedure7
drawSideprocedureName?(third, targuments?) ' call procedure8
t.turnprocedureName?(-60arguments?) ' call procedure9
drawSideprocedureName?(third, targuments?) ' call procedure10
Else11
t.moveprocedureName?(lengtharguments?) ' call procedure12
End If
End Sub
public class Global {
+static void drawSidename?(double length, Turtle tparameter definitions?) {
// procedure1
+if ((length > 1)condition?) {
2
var thirdname? = length/3value or expression?;3
drawSideprocedureName?(third, targuments?); // call procedure4
t.turnprocedureName?(-60arguments?); // call procedure5
drawSideprocedureName?(third, targuments?); // call procedure6
t.turnprocedureName?(120arguments?); // call procedure7
drawSideprocedureName?(third, targuments?); // call procedure8
t.turnprocedureName?(-60arguments?); // call procedure9
drawSideprocedureName?(third, targuments?); // call procedure10
} else {11
t.moveprocedureName?(lengtharguments?); // call procedure12
} // if
} // procedure
}
Function
Examples of functions
From the Snake - procedural demo. Given the coordinates of the snake's head, determines
whether or not the head has gone outside the bounds of the 40x30 grid:
+function hasHitEdgename?(headX as Int, headY as Intparameter definitions?) returns BooleanType?
1
return (headX < 0) or (headY < 0) or (headX > 39) or (headY > 29)value or expression?2
end function
+def hasHitEdgename?(headX: int, headY: intparameter definitions?) -> boolType?:
# function1
return (headX < 0) or (headY < 0) or (headX > 39) or (headY > 29)value or expression?2
+static boolType? hasHitEdgename?(int headX, int headYparameter definitions?) {
// function1
return (headX < 0) || (headY < 0) || (headX > 39) || (headY > 29)value or expression?;2
} // function
+Function hasHitEdgename?(headX As Integer, headY As Integerparameter definitions?) As BooleanType?
1
Return (headX < 0) Or (headY < 0) Or (headX > 39) Or (headY > 29)value or expression?2
End Function
public class Global {
+static boolType? hasHitEdgename?(int headX, int headYparameter definitions?) {
// function1
return (headX < 0) || (headY < 0) || (headX > 39) || (headY > 29)value or expression?;2
} // function
}
It is called within the main routine as part of this instruction:
reassign gameOnvariableName? to not hasHitEdge(head[0], head[1]) and not body.contains(head)value or expression?0
gameOnvariableName? = not hasHitEdge(head[0], head[1]) and not body.contains(head)value or expression? # reassign variable0
gameOnvariableName? = !hasHitEdge(head[0], head[1]) && !body.contains(head)value or expression?; // reassign variable0
gameOnvariableName? = Not hasHitEdge(head[0], head[1]) And Not body.contains(head)value or expression? ' reassign variable0
gameOnvariableName? = !hasHitEdge(head[0], head[1]) && !body.contains(head)value or expression?; // reassign variable0
From the Date Time demo. A function to determine whether a given year is a leap year or not:
+function leapname?(year as Intparameter definitions?) returns BooleanType?
1
return (((year mod 4) is 0) and ((year mod 100) isnt 0)) or ((year mod 400) is 0)value or expression?2
end function
+def leapname?(year: intparameter definitions?) -> boolType?:
# function1
return (((year % 4) == 0) and ((year % 100) != 0)) or ((year % 400) == 0)value or expression?2
+static boolType? leapname?(int yearparameter definitions?) {
// function1
return (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0)value or expression?;2
} // function
+Function leapname?(year As Integerparameter definitions?) As BooleanType?
1
Return (((year Mod 4) = 0) And ((year Mod 100) <> 0)) Or ((year Mod 400) = 0)value or expression?2
End Function
public class Global {
+static boolType? leapname?(int yearparameter definitions?) {
// function1
return (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0)value or expression?;2
} // function
}
From the Life - procedural demo a function that finds the cell immediately to the north of the cell provided. Both
the single parameter and return value are 2-tuples, where both elements are of type IntintintIntegerint:
+function northname?(cell as (Int, Int)parameter definitions?) returns (Int, Int)Type?
1
variable xname? set to cell.item_0value or expression?2
variable yname? set to cell.item_1value or expression?3
variable y2name? set to y - 1value or expression?4
+if y2 is -1condition? then
5
reassign y2variableName? to 29value or expression?6
end if
return (x, y2)value or expression?7
end function
+def northname?(cell: tuple[int, int]parameter definitions?) -> tuple[int, int]Type?:
# function1
xname? = cell.item_0value or expression? # variable definition2
yname? = cell.item_1value or expression? # variable definition3
y2name? = y - 1value or expression? # variable definition4
+if y2 == -1condition?:
5
y2variableName? = 29value or expression? # reassign variable6
return (x, y2)value or expression?7
+static (int, int)Type? northname?((int, int) cellparameter definitions?) {
// function1
var xname? = cell.item_0value or expression?;2
var yname? = cell.item_1value or expression?;3
var y2name? = y - 1value or expression?;4
+if (y2 == -1condition?) {
5
y2variableName? = 29value or expression?; // reassign variable6
} // if
return (x, y2)value or expression?;7
} // function
+Function northname?(cell As (Integer, Integer)parameter definitions?) As (Integer, Integer)Type?
1
Dim xname? = cell.item_0value or expression? ' variable definition2
Dim yname? = cell.item_1value or expression? ' variable definition3
Dim y2name? = y - 1value or expression? ' variable definition4
+If y2 = -1condition? Then
5
y2variableName? = 29value or expression? ' reassign variable6
End If
Return (x, y2)value or expression?7
End Function
public class Global {
+static (int, int)Type? northname?((int, int) cellparameter definitions?) {
// function1
var xname? = cell.item_0value or expression?;2
var yname? = cell.item_1value or expression?;3
var y2name? = y - 1value or expression?;4
+if (y2 == -1condition?) {
5
y2variableName? = 29value or expression?; // reassign variable6
} // if
return (x, y2)value or expression?;7
} // function
}
A function to search a (pre-sorted) list of strings using the binary search algorithm:
+function binarySearchname?(li as List<of String>, item as Stringparameter definitions?) returns BooleanType?
1
variable resultname? set to falsevalue or expression?2
+if li.length() > 0condition? then
3
variable midname? set to divAsInt(li.length(), 2)value or expression?4
variable valuename? set to li[mid]value or expression?5
+if item.equals(value)condition? then
6
reassign resultvariableName? to truevalue or expression?7
elif item.isBefore(value)condition? then8
reassign resultvariableName? to binarySearch(li.subList(0, mid), item)value or expression?9
else10
reassign resultvariableName? to binarySearch(li.subList(mid + 1, li.length()), item)value or expression?11
end if
end if
return resultvalue or expression?12
end function
+def binarySearchname?(li: list[str], item: strparameter definitions?) -> boolType?:
# function1
resultname? = Falsevalue or expression? # variable definition2
+if li.length() > 0condition?:
3
midname? = divAsInt(li.length(), 2)value or expression? # variable definition4
valuename? = li[mid]value or expression? # variable definition5
+if item.equals(value)condition?:
6
resultvariableName? = Truevalue or expression? # reassign variable7
elif item.isBefore(value)condition?: # else if8
resultvariableName? = binarySearch(li.subList(0, mid), item)value or expression? # reassign variable9
else:10
resultvariableName? = binarySearch(li.subList(mid + 1, li.length()), item)value or expression? # reassign variable11
return resultvalue or expression?12
+static boolType? binarySearchname?(List<string> li, string itemparameter definitions?) {
// function1
var resultname? = falsevalue or expression?;2
+if (li.length() > 0condition?) {
3
var midname? = divAsInt(li.length(), 2)value or expression?;4
var valuename? = li[mid]value or expression?;5
+if (item.equals(value)condition?) {
6
resultvariableName? = truevalue or expression?; // reassign variable7
} else if (item.isBefore(value)condition?) {8
resultvariableName? = binarySearch(li.subList(0, mid), item)value or expression?; // reassign variable9
} else {10
resultvariableName? = binarySearch(li.subList(mid + 1, li.length()), item)value or expression?; // reassign variable11
} // if
} // if
return resultvalue or expression?;12
} // function
+Function binarySearchname?(li As List(Of String), item As Stringparameter definitions?) As BooleanType?
1
Dim resultname? = Falsevalue or expression? ' variable definition2
+If li.length() > 0condition? Then
3
Dim midname? = divAsInt(li.length(), 2)value or expression? ' variable definition4
Dim valuename? = li[mid]value or expression? ' variable definition5
+If item.equals(value)condition? Then
6
resultvariableName? = Truevalue or expression? ' reassign variable7
ElseIf item.isBefore(value)condition? Then8
resultvariableName? = binarySearch(li.subList(0, mid), item)value or expression? ' reassign variable9
Else10
resultvariableName? = binarySearch(li.subList(mid + 1, li.length()), item)value or expression? ' reassign variable11
End If
End If
Return resultvalue or expression?12
End Function
public class Global {
+static boolType? binarySearchname?(List<String> li, String itemparameter definitions?) {
// function1
var resultname? = falsevalue or expression?;2
+if (li.length() > 0condition?) {
3
var midname? = divAsInt(li.length(), 2)value or expression?;4
var valuename? = li[mid]value or expression?;5
+if (item.equals(value)condition?) {
6
resultvariableName? = truevalue or expression?; // reassign variable7
} else if (item.isBefore(value)condition?) {8
resultvariableName? = binarySearch(li.subList(0, mid), item)value or expression?; // reassign variable9
} else {10
resultvariableName? = binarySearch(li.subList(mid + 1, li.length()), item)value or expression?; // reassign variable11
} // if
} // if
return resultvalue or expression?;12
} // function
}
What you need to know about functions
- A function defines a mechanism to transform input data (passed as parameters) into a 'returned' value.
- Because it generates a value, a function call is a kind of expression - it may define a complete expression,
a part of a more complex expression (as above).
- The Elan library provides some ready-made functions such as sqrt.
- The function instruction allows you to define your own functions.
- A function is defined with a unique name, starting lower-case (hasHitEdge).
- It will usually define one or more parameters (two in the example above), comma-separated if more than one.
- Each parameter specifies a name starting lower-case (
headXheadXheadXheadXheadX above) and a type (IntintintIntegerint above) using the syntax
headX as Int
Code does not parse as Elan.headX as Int
Code does not parse as Elan.headX as Int
Code does not parse as Elan.headX as Int
Code does not parse as Elan.headX as Int
Code does not parse as Elan.
- The function signature (top line) must also specify the type of value it will return (
returnsreturnsreturnsreturnsreturns BooleanboolboolBooleanbool above).
- The returned value may be a simple value such as an
IntintintIntegerint, or it may be a data structure such as a List<of Float>list[float]List<double>List(Of Double)List<double>.
- A function always has a
returnreturnreturnreturnreturn statement as its last instruction, which specifies a value, or an expression to be evaluated.
The value, or the result of evaluating the expression, must yield the same type as specified in the signature.
- Even though some languages would allow you to put the
returnreturnreturnreturnreturn anywhere within the function, or even have more than one of them,
Elan deliberately does not permit this - in any of the supported languages - because that would result in poorly-structured code.
- The body of a function may consist only of the return statement - with the expression following
returnreturnreturnreturnreturn doing
all the work needed.
- More generally, the return statement will be preceded by other statement instructions to determine the result in several steps.
- While many languages treat functions as a procedure that returns a value - this is unhelpful. Elan enforces - for each of the supported
languages - that functions are kept 'pure', like all functions used in mathematics. Specifically:
- a function cannot undertake any input/output, or create any other 'side effect' (change to the system that can be observed outside the function).
- prohibited side-effects including changing any of the values passed in as parameters.
- the returned result must depend solely, and deterministically, on the values provided as parameters - just as
sqrt(2)sqrt(2)sqrt(2)sqrt(2)sqrt(2)
always produces the same result, irrespective of whatever else might be occurring in the program.
Test
Examples of tests
From the Date Time demo, testing the function that determines whether a given year is a leap year.
Note that the comments distinguish between the 'normal' test cases and the 'boundary' (also known as 'edge') cases,
where errors are more common:
+test test_leaptest_name?
1
#
assert leap(2025)actual (computed) value? is falseexpected value? not run2
assert leap(2024)actual (computed) value? is trueexpected value? not run3
#
assert leap(1900)actual (computed) value? is falseexpected value? not run4
assert leap(2000)actual (computed) value? is trueexpected value? not run5
end test
+def test_leaptest_name?(self) -> None:
1
#
self.assertEqual(leap(2025)actual (computed) value?, Falseexpected value?) not run2
self.assertEqual(leap(2024)actual (computed) value?, Trueexpected value?) not run3
#
self.assertEqual(leap(1900)actual (computed) value?, Falseexpected value?) not run4
self.assertEqual(leap(2000)actual (computed) value?, Trueexpected value?) not run5
+[TestMethod] static void test_leaptest_name?() {
1
//
Assert.AreEqual(falseexpected value?, leap(2025)actual (computed) value?); not run2
Assert.AreEqual(trueexpected value?, leap(2024)actual (computed) value?); not run3
//
Assert.AreEqual(falseexpected value?, leap(1900)actual (computed) value?); not run4
Assert.AreEqual(trueexpected value?, leap(2000)actual (computed) value?); not run5
} // test
+<TestMethod> Sub test_leaptest_name?()
1
'
Assert.AreEqual(Falseexpected value?, leap(2025)actual (computed) value?) not run2
Assert.AreEqual(Trueexpected value?, leap(2024)actual (computed) value?) not run3
'
Assert.AreEqual(Falseexpected value?, leap(1900)actual (computed) value?) not run4
Assert.AreEqual(Trueexpected value?, leap(2000)actual (computed) value?) not run5
End Sub
public class Global {
+@Test static void test_leaptest_name?() {
1
//
assertEquals(falseexpected value?, leap(2025)actual (computed) value?); not run2
assertEquals(trueexpected value?, leap(2024)actual (computed) value?); not run3
//
assertEquals(falseexpected value?, leap(1900)actual (computed) value?); not run4
assertEquals(trueexpected value?, leap(2000)actual (computed) value?); not run5
} // test
}
Example from the Life - procedural demo. It tests all valid combinations of parameter values -
because the logic of this function is quite unusual:
+test test_willLivetest_name?
1
assert willLive(white, 0)actual (computed) value? is falseexpected value? not run2
assert willLive(white, 1)actual (computed) value? is falseexpected value? not run3
assert willLive(white, 2)actual (computed) value? is falseexpected value? not run4
assert willLive(white, 3)actual (computed) value? is trueexpected value? not run5
assert willLive(white, 4)actual (computed) value? is falseexpected value? not run6
assert willLive(white, 5)actual (computed) value? is falseexpected value? not run7
assert willLive(white, 6)actual (computed) value? is falseexpected value? not run8
assert willLive(white, 7)actual (computed) value? is falseexpected value? not run9
assert willLive(white, 8)actual (computed) value? is falseexpected value? not run10
assert willLive(black, 0)actual (computed) value? is falseexpected value? not run11
assert willLive(black, 1)actual (computed) value? is falseexpected value? not run12
assert willLive(black, 2)actual (computed) value? is trueexpected value? not run13
assert willLive(black, 3)actual (computed) value? is trueexpected value? not run14
assert willLive(black, 4)actual (computed) value? is falseexpected value? not run15
assert willLive(black, 5)actual (computed) value? is falseexpected value? not run16
assert willLive(black, 6)actual (computed) value? is falseexpected value? not run17
assert willLive(black, 7)actual (computed) value? is falseexpected value? not run18
assert willLive(black, 8)actual (computed) value? is falseexpected value? not run19
end test
+def test_willLivetest_name?(self) -> None:
1
self.assertEqual(willLive(white, 0)actual (computed) value?, Falseexpected value?) not run2
self.assertEqual(willLive(white, 1)actual (computed) value?, Falseexpected value?) not run3
self.assertEqual(willLive(white, 2)actual (computed) value?, Falseexpected value?) not run4
self.assertEqual(willLive(white, 3)actual (computed) value?, Trueexpected value?) not run5
self.assertEqual(willLive(white, 4)actual (computed) value?, Falseexpected value?) not run6
self.assertEqual(willLive(white, 5)actual (computed) value?, Falseexpected value?) not run7
self.assertEqual(willLive(white, 6)actual (computed) value?, Falseexpected value?) not run8
self.assertEqual(willLive(white, 7)actual (computed) value?, Falseexpected value?) not run9
self.assertEqual(willLive(white, 8)actual (computed) value?, Falseexpected value?) not run10
self.assertEqual(willLive(black, 0)actual (computed) value?, Falseexpected value?) not run11
self.assertEqual(willLive(black, 1)actual (computed) value?, Falseexpected value?) not run12
self.assertEqual(willLive(black, 2)actual (computed) value?, Trueexpected value?) not run13
self.assertEqual(willLive(black, 3)actual (computed) value?, Trueexpected value?) not run14
self.assertEqual(willLive(black, 4)actual (computed) value?, Falseexpected value?) not run15
self.assertEqual(willLive(black, 5)actual (computed) value?, Falseexpected value?) not run16
self.assertEqual(willLive(black, 6)actual (computed) value?, Falseexpected value?) not run17
self.assertEqual(willLive(black, 7)actual (computed) value?, Falseexpected value?) not run18
self.assertEqual(willLive(black, 8)actual (computed) value?, Falseexpected value?) not run19
+[TestMethod] static void test_willLivetest_name?() {
1
Assert.AreEqual(falseexpected value?, willLive(white, 0)actual (computed) value?); not run2
Assert.AreEqual(falseexpected value?, willLive(white, 1)actual (computed) value?); not run3
Assert.AreEqual(falseexpected value?, willLive(white, 2)actual (computed) value?); not run4
Assert.AreEqual(trueexpected value?, willLive(white, 3)actual (computed) value?); not run5
Assert.AreEqual(falseexpected value?, willLive(white, 4)actual (computed) value?); not run6
Assert.AreEqual(falseexpected value?, willLive(white, 5)actual (computed) value?); not run7
Assert.AreEqual(falseexpected value?, willLive(white, 6)actual (computed) value?); not run8
Assert.AreEqual(falseexpected value?, willLive(white, 7)actual (computed) value?); not run9
Assert.AreEqual(falseexpected value?, willLive(white, 8)actual (computed) value?); not run10
Assert.AreEqual(falseexpected value?, willLive(black, 0)actual (computed) value?); not run11
Assert.AreEqual(falseexpected value?, willLive(black, 1)actual (computed) value?); not run12
Assert.AreEqual(trueexpected value?, willLive(black, 2)actual (computed) value?); not run13
Assert.AreEqual(trueexpected value?, willLive(black, 3)actual (computed) value?); not run14
Assert.AreEqual(falseexpected value?, willLive(black, 4)actual (computed) value?); not run15
Assert.AreEqual(falseexpected value?, willLive(black, 5)actual (computed) value?); not run16
Assert.AreEqual(falseexpected value?, willLive(black, 6)actual (computed) value?); not run17
Assert.AreEqual(falseexpected value?, willLive(black, 7)actual (computed) value?); not run18
Assert.AreEqual(falseexpected value?, willLive(black, 8)actual (computed) value?); not run19
} // test
+<TestMethod> Sub test_willLivetest_name?()
1
Assert.AreEqual(Falseexpected value?, willLive(white, 0)actual (computed) value?) not run2
Assert.AreEqual(Falseexpected value?, willLive(white, 1)actual (computed) value?) not run3
Assert.AreEqual(Falseexpected value?, willLive(white, 2)actual (computed) value?) not run4
Assert.AreEqual(Trueexpected value?, willLive(white, 3)actual (computed) value?) not run5
Assert.AreEqual(Falseexpected value?, willLive(white, 4)actual (computed) value?) not run6
Assert.AreEqual(Falseexpected value?, willLive(white, 5)actual (computed) value?) not run7
Assert.AreEqual(Falseexpected value?, willLive(white, 6)actual (computed) value?) not run8
Assert.AreEqual(Falseexpected value?, willLive(white, 7)actual (computed) value?) not run9
Assert.AreEqual(Falseexpected value?, willLive(white, 8)actual (computed) value?) not run10
Assert.AreEqual(Falseexpected value?, willLive(black, 0)actual (computed) value?) not run11
Assert.AreEqual(Falseexpected value?, willLive(black, 1)actual (computed) value?) not run12
Assert.AreEqual(Trueexpected value?, willLive(black, 2)actual (computed) value?) not run13
Assert.AreEqual(Trueexpected value?, willLive(black, 3)actual (computed) value?) not run14
Assert.AreEqual(Falseexpected value?, willLive(black, 4)actual (computed) value?) not run15
Assert.AreEqual(Falseexpected value?, willLive(black, 5)actual (computed) value?) not run16
Assert.AreEqual(Falseexpected value?, willLive(black, 6)actual (computed) value?) not run17
Assert.AreEqual(Falseexpected value?, willLive(black, 7)actual (computed) value?) not run18
Assert.AreEqual(Falseexpected value?, willLive(black, 8)actual (computed) value?) not run19
End Sub
public class Global {
+@Test static void test_willLivetest_name?() {
1
assertEquals(falseexpected value?, willLive(white, 0)actual (computed) value?); not run2
assertEquals(falseexpected value?, willLive(white, 1)actual (computed) value?); not run3
assertEquals(falseexpected value?, willLive(white, 2)actual (computed) value?); not run4
assertEquals(trueexpected value?, willLive(white, 3)actual (computed) value?); not run5
assertEquals(falseexpected value?, willLive(white, 4)actual (computed) value?); not run6
assertEquals(falseexpected value?, willLive(white, 5)actual (computed) value?); not run7
assertEquals(falseexpected value?, willLive(white, 6)actual (computed) value?); not run8
assertEquals(falseexpected value?, willLive(white, 7)actual (computed) value?); not run9
assertEquals(falseexpected value?, willLive(white, 8)actual (computed) value?); not run10
assertEquals(falseexpected value?, willLive(black, 0)actual (computed) value?); not run11
assertEquals(falseexpected value?, willLive(black, 1)actual (computed) value?); not run12
assertEquals(trueexpected value?, willLive(black, 2)actual (computed) value?); not run13
assertEquals(trueexpected value?, willLive(black, 3)actual (computed) value?); not run14
assertEquals(falseexpected value?, willLive(black, 4)actual (computed) value?); not run15
assertEquals(falseexpected value?, willLive(black, 5)actual (computed) value?); not run16
assertEquals(falseexpected value?, willLive(black, 6)actual (computed) value?); not run17
assertEquals(falseexpected value?, willLive(black, 7)actual (computed) value?); not run18
assertEquals(falseexpected value?, willLive(black, 8)actual (computed) value?); not run19
} // test
}
Example from the Snake - procedural demo, showing the use of a variable to create data to be used within multiple test cases (asserts):
+test test_getAdjacentSquaretest_name?
1
variable sqname? set to [20, 15]value or expression?2
assert getAdjacentSquare(sq, Direction.up)actual (computed) value? is [20, 14]expected value? not run3
assert getAdjacentSquare(sq, Direction.down)actual (computed) value? is [20, 16]expected value? not run4
assert getAdjacentSquare(sq, Direction.left)actual (computed) value? is [19, 15]expected value? not run5
assert getAdjacentSquare(sq, Direction.right)actual (computed) value? is [21, 15]expected value? not run6
#
assert getAdjacentSquare([0, 15], Direction.left)actual (computed) value? is [-1, 15]expected value? not run7
end test
+def test_getAdjacentSquaretest_name?(self) -> None:
1
sqname? = [20, 15]value or expression? # variable definition2
self.assertEqual(getAdjacentSquare(sq, Direction.up)actual (computed) value?, [20, 14]expected value?) not run3
self.assertEqual(getAdjacentSquare(sq, Direction.down)actual (computed) value?, [20, 16]expected value?) not run4
self.assertEqual(getAdjacentSquare(sq, Direction.left)actual (computed) value?, [19, 15]expected value?) not run5
self.assertEqual(getAdjacentSquare(sq, Direction.right)actual (computed) value?, [21, 15]expected value?) not run6
#
self.assertEqual(getAdjacentSquare([0, 15], Direction.left)actual (computed) value?, [-1, 15]expected value?) not run7
+[TestMethod] static void test_getAdjacentSquaretest_name?() {
1
var sqname? = [20, 15]value or expression?;2
Assert.AreEqual([20, 14]expected value?, getAdjacentSquare(sq, Direction.up)actual (computed) value?); not run3
Assert.AreEqual([20, 16]expected value?, getAdjacentSquare(sq, Direction.down)actual (computed) value?); not run4
Assert.AreEqual([19, 15]expected value?, getAdjacentSquare(sq, Direction.left)actual (computed) value?); not run5
Assert.AreEqual([21, 15]expected value?, getAdjacentSquare(sq, Direction.right)actual (computed) value?); not run6
//
Assert.AreEqual([-1, 15]expected value?, getAdjacentSquare([0, 15], Direction.left)actual (computed) value?); not run7
} // test
+<TestMethod> Sub test_getAdjacentSquaretest_name?()
1
Dim sqname? = {20, 15}value or expression? ' variable definition2
Assert.AreEqual({20, 14}expected value?, getAdjacentSquare(sq, Direction.up)actual (computed) value?) not run3
Assert.AreEqual({20, 16}expected value?, getAdjacentSquare(sq, Direction.down)actual (computed) value?) not run4
Assert.AreEqual({19, 15}expected value?, getAdjacentSquare(sq, Direction.left)actual (computed) value?) not run5
Assert.AreEqual({21, 15}expected value?, getAdjacentSquare(sq, Direction.right)actual (computed) value?) not run6
'
Assert.AreEqual({-1, 15}expected value?, getAdjacentSquare({0, 15}, Direction.left)actual (computed) value?) not run7
End Sub
public class Global {
+@Test static void test_getAdjacentSquaretest_name?() {
1
var sqname? = [20, 15]value or expression?;2
assertEquals([20, 14]expected value?, getAdjacentSquare(sq, Direction.up)actual (computed) value?); not run3
assertEquals([20, 16]expected value?, getAdjacentSquare(sq, Direction.down)actual (computed) value?); not run4
assertEquals([19, 15]expected value?, getAdjacentSquare(sq, Direction.left)actual (computed) value?); not run5
assertEquals([21, 15]expected value?, getAdjacentSquare(sq, Direction.right)actual (computed) value?); not run6
//
assertEquals([-1, 15]expected value?, getAdjacentSquare([0, 15], Direction.left)actual (computed) value?); not run7
} // test
}
What you need to know about tests
- The form of automated testing provided by Elan (for all of the supported languages) are known as 'unit testing'.
- Some languages allow unit tests to be applied to procedures, but this dangerous since running the tests could result
in unwanted damage to the system (such as writing to a database); Elan guarantees that tests are safe by restrict them to functions.
- A test method must be given a name that starts with the prefix `test_` (added automatically) and followed, typically,
by the name of the function.
- Tests are always written at global level in Elan, but they may be anywhere within the file: you may write a test
adjacent to the function being tested, or group all the tests at the end of the file perhaps.
- A test method may define variables for use in the test - for example to define a data value that will be used in several asserts.
- A test method must include one or more assert statements - with each one typically covering a different use-case.
- Each assert instruction has two fields that the programmer must complete: the actual (computed) result - which typically
consists of a call to the function being tested, with specific arguments - and the expected result.
- The tests do not form part of the executable program: you cannot call a test from within other code.
- Instead, all tests in a program are run automatically, each time the code compiles.
- The result of each assert in each test is shown alongside the assert instruction, and the overall summary is shown in the
status panel at the top centre of the Elan IDE.
- Writing tests for every function is good practice - confirming that your function implementations are correct: when you write them,
every time you re-load the program from file.
- Tests also encourage you to 're-factor', improve, or extend the capability of your functions without fear that you
have inadvertently made then incorrect for certain cases.
More advanced techniques
Ghosting tests
To deliberately ensure that a test is not run, use ghostingghostingghostingghostingghosting .
Even when tests or assert equals are ghostedghostedghostedghostedghosted, 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).
You can ghost 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
+[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
} // test
+<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
public class Global {
+@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
} // test
}
or you can ghost any individual assert(s):
+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
+[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
} // test
+<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
public class Global {
+@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
} // test
}
Testing FloatfloatdoubleDoubledouble values
When testing FloatfloatdoubleDoubledouble values it is recommend that you use the round method to round the computed result to a fixed number of decimal places. This avoids rounding errors and is easier to read. For example:
+test test_roundtest_name?
1
assert sqrt(2).round(3)actual (computed) value? is 1.414expected value? not run2
end test
+def test_roundtest_name?(self) -> None:
1
self.assertEqual(sqrt(2).round(3)actual (computed) value?, 1.414expected value?) not run2
+[TestMethod] static void test_roundtest_name?() {
1
Assert.AreEqual(1.414expected value?, sqrt(2).round(3)actual (computed) value?); not run2
} // test
+<TestMethod> Sub test_roundtest_name?()
1
Assert.AreEqual(1.414expected value?, sqrt(2).round(3)actual (computed) value?) not run2
End Sub
public class Global {
+@Test static void test_roundtest_name?() {
1
assertEquals(1.414expected value?, sqrt(2).round(3)actual (computed) value?); not run2
} // test
}
Testing for runtime errors
If the expression you are testing would cause a runtime error then the error will be displayed in the red fail message:
If there are failures, mark the tests that you added since the last successful test as ghostedghostedghostedghostedghosted and then remove their ghosted status one by one until the cause is identified and fixed.
+test test_listtest_name?
1
variable aname? set to [5, 1, 7]value or expression?2
assert a[0]actual (computed) value? is 5expected value? not run3
assert a[2]actual (computed) value? is 7expected value? not run4
end test
+def test_listtest_name?(self) -> None:
1
aname? = [5, 1, 7]value or expression? # variable definition2
self.assertEqual(a[0]actual (computed) value?, 5expected value?) not run3
self.assertEqual(a[2]actual (computed) value?, 7expected value?) not run4
+[TestMethod] static void test_listtest_name?() {
1
var aname? = [5, 1, 7]value or expression?;2
Assert.AreEqual(5expected value?, a[0]actual (computed) value?); not run3
Assert.AreEqual(7expected value?, a[2]actual (computed) value?); not run4
} // test
+<TestMethod> Sub test_listtest_name?()
1
Dim aname? = {5, 1, 7}value or expression? ' variable definition2
Assert.AreEqual(5expected value?, a[0]actual (computed) value?) not run3
Assert.AreEqual(7expected value?, a[2]actual (computed) value?) not run4
End Sub
public class Global {
+@Test static void test_listtest_name?() {
1
var aname? = [5, 1, 7]value or expression?;2
assertEquals(5expected value?, a[0]actual (computed) value?); not run3
assertEquals(7expected value?, a[2]actual (computed) value?); not run4
} // test
}
This assert shows how to test for an expected error message:
+test test_messagetest_name?
1
variable aname? set to [5, 1, 7]value or expression?2
assert a[4]actual (computed) value? is 0expected value? not run3
assert a[4]actual (computed) value? is "Out of range index: 4 size: 3"expected value? not run4
end test
+def test_messagetest_name?(self) -> None:
1
aname? = [5, 1, 7]value or expression? # variable definition2
self.assertEqual(a[4]actual (computed) value?, 0expected value?) not run3
self.assertEqual(a[4]actual (computed) value?, "Out of range index: 4 size: 3"expected value?) not run4
+[TestMethod] static void test_messagetest_name?() {
1
var aname? = [5, 1, 7]value or expression?;2
Assert.AreEqual(0expected value?, a[4]actual (computed) value?); not run3
Assert.AreEqual("Out of range index: 4 size: 3"expected value?, a[4]actual (computed) value?); not run4
} // test
+<TestMethod> Sub test_messagetest_name?()
1
Dim aname? = {5, 1, 7}value or expression? ' variable definition2
Assert.AreEqual(0expected value?, a[4]actual (computed) value?) not run3
Assert.AreEqual("Out of range index: 4 size: 3"expected value?, a[4]actual (computed) value?) not run4
End Sub
public class Global {
+@Test static void test_messagetest_name?() {
1
var aname? = [5, 1, 7]value or expression?;2
assertEquals(0expected value?, a[4]actual (computed) value?); not run3
assertEquals("Out of range index: 4 size: 3"expected value?, a[4]actual (computed) value?); not run4
} // test
}
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. In the following example, the
programmer has accidentally missed out the `/` in the closing html tag:
+constant sGWname? set to "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"literal value or data structure?1
+function setInStylename?(s as Stringparameter definitions?) returns StringType?
2
return "<style>" + s + "<style>"value or expression?3
end function
+test test_setInStyletest_name?
4
assert setInStyle(sGW)actual (computed) value? is "<style>" + sGW + "<style>"expected value? not run5
#
end test
+sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"literal value or data structure? # constant1
+def setInStylename?(s: strparameter definitions?) -> strType?:
# function2
return "<style>" + s + "<style>"value or expression?3
+def test_setInStyletest_name?(self) -> None:
4
self.assertEqual(setInStyle(sGW)actual (computed) value?, "<style>" + sGW + "<style>"expected value?) not run5
#
+const String sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"literal value or data structure?;1
+static stringType? setInStylename?(string sparameter definitions?) {
// function2
return "<style>" + s + "<style>"value or expression?;3
} // function
+[TestMethod] static void test_setInStyletest_name?() {
4
Assert.AreEqual("<style>" + sGW + "<style>"expected value?, setInStyle(sGW)actual (computed) value?); not run5
//
} // test
+Const sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"literal value or data structure?1
+Function setInStylename?(s As Stringparameter definitions?) As StringType?
2
Return "<style>" + s + "<style>"value or expression?3
End Function
+<TestMethod> Sub test_setInStyletest_name?()
4
Assert.AreEqual("<style>" + sGW + "<style>"expected value?, setInStyle(sGW)actual (computed) value?) not run5
'
End Sub
public class Global {
+final String sGWname? = "grid { display: flex; flex-direction: column; margin-top: 40px; width: 500px; } word { display: flex; flex-direction: row; margin: auto; }"literal value or data structure?; // constant1
+static StringType? setInStylename?(String sparameter definitions?) {
// function2
return "<style>" + s + "<style>"value or expression?;3
} // function
+@Test static void test_setInStyletest_name?() {
4
assertEquals("<style>" + sGW + "<style>"expected value?, setInStyle(sGW)actual (computed) value?); not run5
//
} // test
}
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 testtesttesttesttest.
Constant
Examples of constants
From the Turtle Snowflake demo. The constant value sidesidesidesideside is defined here:
+constant sidename? set to 100literal value or data structure?1
+sidename? = 100literal value or data structure? # constant1
+const Int sidename? = 100literal value or data structure?;1
+Const sidename? = 100literal value or data structure?1
public class Global {
+final Int sidename? = 100literal value or data structure?; // constant1
}
and used within the main routine as an argument to pass into the drawSide procedure:
call drawSideprocedureName?(side, targuments?)0
drawSideprocedureName?(side, targuments?) # call procedure0
drawSideprocedureName?(side, targuments?); // call procedure0
drawSideprocedureName?(side, targuments?) ' call procedure0
drawSideprocedureName?(side, targuments?); // call procedure0
+constant maxHitsname? set to 10literal value or data structure?1
+constant turquoisename? set to 0x00ced1literal value or data structure?2
+constant liveCellname? set to blackliteral value or data structure?3
+constant speedOfLightname? set to 299792.458literal value or data structure?4
+constant gameOvername? set to trueliteral value or data structure?5
+constant euroname? set to 0x20acliteral value or data structure?6
+constant warningMsgname? set to "Limit reached"literal value or data structure?7
+maxHitsname? = 10literal value or data structure? # constant1
+turquoisename? = 0x00ced1literal value or data structure? # constant2
+liveCellname? = blackliteral value or data structure? # constant3
+speedOfLightname? = 299792.458literal value or data structure? # constant4
+gameOvername? = Trueliteral value or data structure? # constant5
+euroname? = 0x20acliteral value or data structure? # constant6
+warningMsgname? = "Limit reached"literal value or data structure? # constant7
+const Int maxHitsname? = 10literal value or data structure?;1
+const Int turquoisename? = 0x00ced1literal value or data structure?;2
+const Int liveCellname? = blackliteral value or data structure?;3
+const Float speedOfLightname? = 299792.458literal value or data structure?;4
+const Boolean gameOvername? = trueliteral value or data structure?;5
+const Int euroname? = 0x20acliteral value or data structure?;6
+const String warningMsgname? = "Limit reached"literal value or data structure?;7
+Const maxHitsname? = 10literal value or data structure?1
+Const turquoisename? = &H00ced1literal value or data structure?2
+Const liveCellname? = blackliteral value or data structure?3
+Const speedOfLightname? = 299792.458literal value or data structure?4
+Const gameOvername? = Trueliteral value or data structure?5
+Const euroname? = &H20acliteral value or data structure?6
+Const warningMsgname? = "Limit reached"literal value or data structure?7
public class Global {
+final Int maxHitsname? = 10literal value or data structure?; // constant1
+final Int turquoisename? = 0x00ced1literal value or data structure?; // constant2
+final Int liveCellname? = blackliteral value or data structure?; // constant3
+final Float speedOfLightname? = 299792.458literal value or data structure?; // constant4
+final Boolean gameOvername? = trueliteral value or data structure?; // constant5
+final Int euroname? = 0x20acliteral value or data structure?; // constant6
+final String warningMsgname? = "Limit reached"literal value or data structure?; // constant7
}
What you need to know about constants
- A constant is a named value that never changes while a program is running.
- The Elan library defines a few constants including
pipipipipi and a handful of simple colours e.g. redredredredred.
- The constant instruction allows you to define your own constant(s).
- Even though Python does not natively support the idea of a constant, when writing
Python with Elan, having created a constant with constant instruction you will find that you
cannot change it with a reassign instruction.
- When coding with Elan, a constant:
- may be defined only at global (file) level.
- must be given a unique name, starting lower-case (
sidesidesidesideside above).
- must be defined by a liter value, which must be a number, string, or Boolean (true/false).
- If you define your own constant with the same name as a library constant, you will not be able to access
the library one within your program. If you need to do this: rename your own version.
More advanced techniques
Constant data structures
To use a data structure that is to be a constant in your program, you define it as the value returned function
that defines no parameters. This function can then be referenced from anywhere in the program, often from within an expression
but you need to remember to and empty brackets after the name, when using it.
The following examples define a constant list of colours (using numerical hex values), and a constant dictionary that specifies
the colour for each suit in a deck of cards:
+main
1
call printprocedureName?($"{rainbow()[0]} is violet"arguments?)2
call printprocedureName?($"{suitColours()["hearts"]} is red"arguments?)3
end main
+function rainbowname?(parameter definitionsparameter definitions?) returns List<of Int>Type?
4
return [0x9400D3, 0x4B0082, 0x0000CD, 0x008000, 0xFFFF00, 0xFFA500, 0xFF0000]value or expression?5
end function
+function suitColoursname?(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
printprocedureName?(f"{rainbow()[0]} is violet"arguments?)2
printprocedureName?(f"{suitColours()["hearts"]} is red"arguments?)3
+def rainbowname?(parameter definitionsparameter definitions?) -> list[int]Type?:
# function4
return [0x9400D3, 0x4B0082, 0x0000CD, 0x008000, 0xFFFF00, 0xFFA500, 0xFF0000]value or expression?5
+def suitColoursname?(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
printprocedureName?($"{rainbow()[0]} is violet"arguments?);2
printprocedureName?($"{suitColours()["hearts"]} is red"arguments?);3
} // main
+static List<int>Type? rainbowname?(parameter definitionsparameter definitions?) {
// function4
return [0x9400D3, 0x4B0082, 0x0000CD, 0x008000, 0xFFFF00, 0xFFA500, 0xFF0000]value or expression?;5
} // function
+static Dictionary<string, int>Type? suitColoursname?(parameter definitionsparameter definitions?) {
// function6
return ["spades":black, "hearts":red, "diamonds":red, "clubs":black]value or expression?;7
} // function
+Sub main()
1
printprocedureName?($"{rainbow()[0]} is violet"arguments?)2
printprocedureName?($"{suitColours()["hearts"]} is red"arguments?)3
End Sub
+Function rainbowname?(parameter definitionsparameter definitions?) As List(Of Integer)Type?
4
Return {&H9400D3, &H4B0082, &H0000CD, &H008000, &HFFFF00, &HFFA500, &HFF0000}value or expression?5
End Function
+Function suitColoursname?(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
public class Global {
+static void main() {
1
printprocedureName?(String.format("% is violet", rainbow()[0])arguments?);2
printprocedureName?(String.format("% is red", suitColours()["hearts"])arguments?);3
} // main
+static List<int>Type? rainbowname?(parameter definitionsparameter definitions?) {
// function4
return [0x9400D3, 0x4B0082, 0x0000CD, 0x008000, 0xFFFF00, 0xFFA500, 0xFF0000]value or expression?;5
} // function
+static Dictionary<String, int>Type? suitColoursname?(parameter definitionsparameter definitions?) {
// function6
return ["spades":black, "hearts":red, "diamonds":red, "clubs":black]value or expression?;7
} // function
}
Enum
Examples of enums
From the Snake - procedural demo, the enum is defined here:
enum DirectionName? up, down, left, rightvalues?values?1
class DirectionName?(Enum):
up = 1
down = 2
left = 3
right = 4values?1
enum DirectionName? {up, down, left, rightvalues?values?}1
Enum DirectionName?
up = 0
down = 1
left = 2
right = 3
End Enumvalues?1
public class Global {
enum DirectionName? {up, down, left, rightvalues?values?}1
}
and is used in several places in the code, for example:
+function getAdjacentSquarename?(sq as List<of Int>, dir as Directionparameter definitions?) returns List<of Int>Type?
1
variable newXname? set to sq[0]value or expression?2
variable newYname? set to sq[1]value or expression?3
+if dir is Direction.leftcondition? then
4
reassign newXvariableName? to newX - 1value or expression?5
elif dir is Direction.rightcondition? then6
reassign newXvariableName? to newX + 1value or expression?7
elif dir is Direction.upcondition? then8
reassign newYvariableName? to newY - 1value or expression?9
elif dir is Direction.downcondition? then10
reassign newYvariableName? to newY + 1value or expression?11
end if
return [newX, newY]value or expression?12
end function
+def getAdjacentSquarename?(sq: list[int], dir: Directionparameter definitions?) -> list[int]Type?:
# function1
newXname? = sq[0]value or expression? # variable definition2
newYname? = sq[1]value or expression? # variable definition3
+if dir == Direction.leftcondition?:
4
newXvariableName? = newX - 1value or expression? # reassign variable5
elif dir == Direction.rightcondition?: # else if6
newXvariableName? = newX + 1value or expression? # reassign variable7
elif dir == Direction.upcondition?: # else if8
newYvariableName? = newY - 1value or expression? # reassign variable9
elif dir == Direction.downcondition?: # else if10
newYvariableName? = newY + 1value or expression? # reassign variable11
return [newX, newY]value or expression?12
+static List<int>Type? getAdjacentSquarename?(List<int> sq, Direction dirparameter definitions?) {
// function1
var newXname? = sq[0]value or expression?;2
var newYname? = sq[1]value or expression?;3
+if (dir == Direction.leftcondition?) {
4
newXvariableName? = newX - 1value or expression?; // reassign variable5
} else if (dir == Direction.rightcondition?) {6
newXvariableName? = newX + 1value or expression?; // reassign variable7
} else if (dir == Direction.upcondition?) {8
newYvariableName? = newY - 1value or expression?; // reassign variable9
} else if (dir == Direction.downcondition?) {10
newYvariableName? = newY + 1value or expression?; // reassign variable11
} // if
return [newX, newY]value or expression?;12
} // function
+Function getAdjacentSquarename?(sq As List(Of Integer), dir As Directionparameter definitions?) As List(Of Integer)Type?
1
Dim newXname? = sq[0]value or expression? ' variable definition2
Dim newYname? = sq[1]value or expression? ' variable definition3
+If dir = Direction.leftcondition? Then
4
newXvariableName? = newX - 1value or expression? ' reassign variable5
ElseIf dir = Direction.rightcondition? Then6
newXvariableName? = newX + 1value or expression? ' reassign variable7
ElseIf dir = Direction.upcondition? Then8
newYvariableName? = newY - 1value or expression? ' reassign variable9
ElseIf dir = Direction.downcondition? Then10
newYvariableName? = newY + 1value or expression? ' reassign variable11
End If
Return {newX, newY}value or expression?12
End Function
public class Global {
+static List<int>Type? getAdjacentSquarename?(List<int> sq, Direction dirparameter definitions?) {
// function1
var newXname? = sq[0]value or expression?;2
var newYname? = sq[1]value or expression?;3
+if (dir == Direction.leftcondition?) {
4
newXvariableName? = newX - 1value or expression?; // reassign variable5
} else if (dir == Direction.rightcondition?) {6
newXvariableName? = newX + 1value or expression?; // reassign variable7
} else if (dir == Direction.upcondition?) {8
newYvariableName? = newY - 1value or expression?; // reassign variable9
} else if (dir == Direction.downcondition?) {10
newYvariableName? = newY + 1value or expression?; // reassign variable11
} // if
return [newX, newY]value or expression?;12
} // function
}
From the
Blackjack demo:
enum SuitName? spades, hearts, diamonds, clubsvalues?values?1
enum OutcomeName? undecided, win, lose, draw, winDoublevalues?values?2
class SuitName?(Enum):
spades = 1
hearts = 2
diamonds = 3
clubs = 4values?1
class OutcomeName?(Enum):
undecided = 1
win = 2
lose = 3
draw = 4
winDouble = 5values?2
enum SuitName? {spades, hearts, diamonds, clubsvalues?values?}1
enum OutcomeName? {undecided, win, lose, draw, winDoublevalues?values?}2
Enum SuitName?
spades = 0
hearts = 1
diamonds = 2
clubs = 3
End Enumvalues?1
Enum OutcomeName?
undecided = 0
win = 1
lose = 2
draw = 3
winDouble = 4
End Enumvalues?2
public class Global {
enum SuitName? {spades, hearts, diamonds, clubsvalues?values?}1
enum OutcomeName? {undecided, win, lose, draw, winDoublevalues?values?}2
}
What you need to know about enums
Statement instructions
Statement instructions (sometimes referred to as 'statements') are the instructions used with in the methods
of a program: the main routine, functions, and procedures.
Print
Examples of print statements
From the Date Time demo, printing the time (as a number of seconds) and then the date by calling the function getDate
inline within the print statement:
+main
1
variable replyname? set to ""value or expression?2
+while not reply.upperCase().equals("Q")condition?
3
reassign replyvariableName? to input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression?4
+if reply.equals("")condition? then
5
variable nowname? set to divAsInt(clock(), 1000)value or expression?6
print(nowarguments?)7
print(getDate(now)arguments?)8
else9
+try
10
variable tdname? set to int(reply)value or expression?11
+if td >= 0condition? then
12
call printprocedureName?(getDate(td)arguments?)13
end if
catch evariableName? as ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError?14
end try
end if
end while
end main
+def main() -> None:
1
replyname? = ""value or expression? # variable definition2
+while not reply.upperCase().equals("Q")condition?:
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression? # reassign variable4
+if reply.equals("")condition?:
5
nowname? = divAsInt(clock(), 1000)value or expression? # variable definition6
print(nowarguments?)7
print(getDate(now)arguments?)8
else:9
+try:
10
tdname? = int(reply)value or expression? # variable definition11
+if td >= 0condition?:
12
printprocedureName?(getDate(td)arguments?)13
except ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? as evariableName?: # catch14
main()
+static void main() {
1
var replyname? = ""value or expression?;2
+while (!reply.upperCase().equals("Q")condition?) {
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression?; // reassign variable4
+if (reply.equals("")condition?) {
5
var nowname? = divAsInt(clock(), 1000)value or expression?;6
Console.WriteLine(nowarguments?); // print7
Console.WriteLine(getDate(now)arguments?); // print8
} else {9
+try {
10
var tdname? = int(reply)value or expression?;11
+if (td >= 0condition?) {
12
printprocedureName?(getDate(td)arguments?);13
} // if
} catch (ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? evariableName?) {14
} // try
} // if
} // while
} // main
+Sub main()
1
Dim replyname? = ""value or expression? ' variable definition2
+While Not reply.upperCase().equals("Q")condition?
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression? ' reassign variable4
+If reply.equals("")condition? Then
5
Dim nowname? = divAsInt(clock(), 1000)value or expression? ' variable definition6
Console.WriteLine(nowarguments?) ' print7
Console.WriteLine(getDate(now)arguments?) ' print8
Else9
+Try
10
Dim tdname? = int(reply)value or expression? ' variable definition11
+If td >= 0condition? Then
12
printprocedureName?(getDate(td)arguments?)13
End If
Catch evariableName? As ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError?14
End Try
End If
End While
End Sub
public class Global {
+static void main() {
1
var replyname? = ""value or expression?;2
+while (!reply.upperCase().equals("Q")condition?) {
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression?; // reassign variable4
+if (reply.equals("")condition?) {
5
var nowname? = divAsInt(clock(), 1000)value or expression?;6
System.out.println(nowarguments?); // print7
System.out.println(getDate(now)arguments?); // print8
} else {9
+try {
10
var tdname? = int(reply)value or expression?;11
+if (td >= 0condition?) {
12
printprocedureName?(getDate(td)arguments?);13
} // if
} catch (ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? evariableName?) {14
} // try
} // if
} // while
} // main
}
From the Ripple sort demo, printing a list:
+main
1
variable liname? set to [7, 1, 0, 4, 8, 3, 6]value or expression?2
print(liarguments?)3
call inPlaceRippleSortprocedureName?(liarguments?)4
call printprocedureName?(liarguments?)5
end main
+def main() -> None:
1
liname? = [7, 1, 0, 4, 8, 3, 6]value or expression? # variable definition2
print(liarguments?)3
inPlaceRippleSortprocedureName?(liarguments?) # call procedure4
printprocedureName?(liarguments?)5
main()
+static void main() {
1
var liname? = [7, 1, 0, 4, 8, 3, 6]value or expression?;2
Console.WriteLine(liarguments?); // print3
inPlaceRippleSortprocedureName?(liarguments?); // call procedure4
printprocedureName?(liarguments?);5
} // main
+Sub main()
1
Dim liname? = {7, 1, 0, 4, 8, 3, 6}value or expression? ' variable definition2
Console.WriteLine(liarguments?) ' print3
inPlaceRippleSortprocedureName?(liarguments?) ' call procedure4
printprocedureName?(liarguments?)5
End Sub
public class Global {
+static void main() {
1
var liname? = [7, 1, 0, 4, 8, 3, 6]value or expression?;2
System.out.println(liarguments?); // print3
inPlaceRippleSortprocedureName?(liarguments?); // call procedure4
printprocedureName?(liarguments?);5
} // main
}
From the Snake - procedural demo, printing an interpolated string (last line of the main routine):
+main
1
variable blocksname? set to createBlockGraphics(white)value or expression?2
variable headname? set to [20, 15]value or expression?3
variable tailname? set to headvalue or expression?4
variable bodyname? set to [head]value or expression?5
variable currentDirname? set to Direction.rightvalue or expression?6
variable gameOnname? set to truevalue or expression?7
variable applename? set to [0, 0]value or expression?8
call setAppleToRandomPositionprocedureName?(apple, bodyarguments?)9
+while gameOncondition?
10
call updateDisplayprocedureName?(blocks, head, tail, body, applearguments?)11
variable currentDirRefname? set to new AsRef<of Direction>(currentDir)value or expression?12
variable headRefname? set to new AsRef<of List<of Int>>(head)value or expression?13
variable tailRefname? set to new AsRef<of List<of Int>>(tail)value or expression?14
call updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?)15
reassign headvariableName? to headRef.value()value or expression?16
reassign tailvariableName? to tailRef.value()value or expression?17
reassign currentDirvariableName? to currentDirRef.value()value or expression?18
reassign gameOnvariableName? to not hasHitEdge(head[0], head[1]) and not body.contains(head)value or expression?19
+if head.equals(apple)condition? then
20
call setAppleToRandomPositionprocedureName?(apple, bodyarguments?)21
else22
call body.removeAtprocedureName?(0arguments?)23
end if
call sleep_msprocedureName?(150arguments?)24
end while
call printprocedureName?($"Game Over! Score: {body.length() - 1}"arguments?)25
end main
+def main() -> None:
1
blocksname? = createBlockGraphics(white)value or expression? # variable definition2
headname? = [20, 15]value or expression? # variable definition3
tailname? = headvalue or expression? # variable definition4
bodyname? = [head]value or expression? # variable definition5
currentDirname? = Direction.rightvalue or expression? # variable definition6
gameOnname? = Truevalue or expression? # variable definition7
applename? = [0, 0]value or expression? # variable definition8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) # call procedure9
+while gameOncondition?:
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?) # call procedure11
currentDirRefname? = AsRef[Direction](currentDir)value or expression? # variable definition12
headRefname? = AsRef[list[int]](head)value or expression? # variable definition13
tailRefname? = AsRef[list[int]](tail)value or expression? # variable definition14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?) # call procedure15
headvariableName? = headRef.value()value or expression? # reassign variable16
tailvariableName? = tailRef.value()value or expression? # reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression? # reassign variable18
gameOnvariableName? = not hasHitEdge(head[0], head[1]) and not body.contains(head)value or expression? # reassign variable19
+if head.equals(apple)condition?:
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) # call procedure21
else:22
body.removeAtprocedureName?(0arguments?) # call procedure23
sleep_msprocedureName?(150arguments?) # call procedure24
printprocedureName?(f"Game Over! Score: {body.length() - 1}"arguments?)25
main()
+static void main() {
1
var blocksname? = createBlockGraphics(white)value or expression?;2
var headname? = [20, 15]value or expression?;3
var tailname? = headvalue or expression?;4
var bodyname? = [head]value or expression?;5
var currentDirname? = Direction.rightvalue or expression?;6
var gameOnname? = truevalue or expression?;7
var applename? = [0, 0]value or expression?;8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure9
+while (gameOncondition?) {
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?); // call procedure11
var currentDirRefname? = new AsRef<Direction>(currentDir)value or expression?;12
var headRefname? = new AsRef<List<int>>(head)value or expression?;13
var tailRefname? = new AsRef<List<int>>(tail)value or expression?;14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?); // call procedure15
headvariableName? = headRef.value()value or expression?; // reassign variable16
tailvariableName? = tailRef.value()value or expression?; // reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression?; // reassign variable18
gameOnvariableName? = !hasHitEdge(head[0], head[1]) && !body.contains(head)value or expression?; // reassign variable19
+if (head.equals(apple)condition?) {
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure21
} else {22
body.removeAtprocedureName?(0arguments?); // call procedure23
} // if
sleep_msprocedureName?(150arguments?); // call procedure24
} // while
printprocedureName?($"Game Over! Score: {body.length() - 1}"arguments?);25
} // main
+Sub main()
1
Dim blocksname? = createBlockGraphics(white)value or expression? ' variable definition2
Dim headname? = {20, 15}value or expression? ' variable definition3
Dim tailname? = headvalue or expression? ' variable definition4
Dim bodyname? = {head}value or expression? ' variable definition5
Dim currentDirname? = Direction.rightvalue or expression? ' variable definition6
Dim gameOnname? = Truevalue or expression? ' variable definition7
Dim applename? = {0, 0}value or expression? ' variable definition8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) ' call procedure9
+While gameOncondition?
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?) ' call procedure11
Dim currentDirRefname? = New AsRef(Of Direction)(currentDir)value or expression? ' variable definition12
Dim headRefname? = New AsRef(Of List(Of Integer))(head)value or expression? ' variable definition13
Dim tailRefname? = New AsRef(Of List(Of Integer))(tail)value or expression? ' variable definition14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?) ' call procedure15
headvariableName? = headRef.value()value or expression? ' reassign variable16
tailvariableName? = tailRef.value()value or expression? ' reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression? ' reassign variable18
gameOnvariableName? = Not hasHitEdge(head[0], head[1]) And Not body.contains(head)value or expression? ' reassign variable19
+If head.equals(apple)condition? Then
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) ' call procedure21
Else22
body.removeAtprocedureName?(0arguments?) ' call procedure23
End If
sleep_msprocedureName?(150arguments?) ' call procedure24
End While
printprocedureName?($"Game Over! Score: {body.length() - 1}"arguments?)25
End Sub
public class Global {
+static void main() {
1
var blocksname? = createBlockGraphics(white)value or expression?;2
var headname? = [20, 15]value or expression?;3
var tailname? = headvalue or expression?;4
var bodyname? = [head]value or expression?;5
var currentDirname? = Direction.rightvalue or expression?;6
var gameOnname? = truevalue or expression?;7
var applename? = [0, 0]value or expression?;8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure9
+while (gameOncondition?) {
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?); // call procedure11
var currentDirRefname? = new AsRef<Direction>(currentDir)value or expression?;12
var headRefname? = new AsRef<List<int>>(head)value or expression?;13
var tailRefname? = new AsRef<List<int>>(tail)value or expression?;14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?); // call procedure15
headvariableName? = headRef.value()value or expression?; // reassign variable16
tailvariableName? = tailRef.value()value or expression?; // reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression?; // reassign variable18
gameOnvariableName? = !hasHitEdge(head[0], head[1]) && !body.contains(head)value or expression?; // reassign variable19
+if (head.equals(apple)condition?) {
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure21
} else {22
body.removeAtprocedureName?(0arguments?); // call procedure23
} // if
sleep_msprocedureName?(150arguments?); // call procedure24
} // while
printprocedureName?(String.format("Game Over! Score: %", body.length() - 1)arguments?);25
} // main
}
What you need to know about print statements
- The print instruction writes a value to display.
- It automatically prints a new line after the value.
- The value can be any expression.
- You must provide a value; if you just want to print a blank line, just set the value to
new codenew codenew codenew codepublic class Global {
new code
}
+main
1
variable replyname? set to ""value or expression?2
+while not reply.upperCase().equals("Q")condition?
3
reassign replyvariableName? to input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression?4
+if reply.equals("")condition? then
5
variable nowname? set to divAsInt(clock(), 1000)value or expression?6
print(nowarguments?)7
print(getDate(now)arguments?)8
else9
+try
10
variable tdname? set to int(reply)value or expression?11
+if td >= 0condition? then
12
call printprocedureName?(getDate(td)arguments?)13
end if
catch evariableName? as ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError?14
end try
end if
end while
end main
+def main() -> None:
1
replyname? = ""value or expression? # variable definition2
+while not reply.upperCase().equals("Q")condition?:
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression? # reassign variable4
+if reply.equals("")condition?:
5
nowname? = divAsInt(clock(), 1000)value or expression? # variable definition6
print(nowarguments?)7
print(getDate(now)arguments?)8
else:9
+try:
10
tdname? = int(reply)value or expression? # variable definition11
+if td >= 0condition?:
12
printprocedureName?(getDate(td)arguments?)13
except ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? as evariableName?: # catch14
main()
+static void main() {
1
var replyname? = ""value or expression?;2
+while (!reply.upperCase().equals("Q")condition?) {
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression?; // reassign variable4
+if (reply.equals("")condition?) {
5
var nowname? = divAsInt(clock(), 1000)value or expression?;6
Console.WriteLine(nowarguments?); // print7
Console.WriteLine(getDate(now)arguments?); // print8
} else {9
+try {
10
var tdname? = int(reply)value or expression?;11
+if (td >= 0condition?) {
12
printprocedureName?(getDate(td)arguments?);13
} // if
} catch (ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? evariableName?) {14
} // try
} // if
} // while
} // main
+Sub main()
1
Dim replyname? = ""value or expression? ' variable definition2
+While Not reply.upperCase().equals("Q")condition?
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression? ' reassign variable4
+If reply.equals("")condition? Then
5
Dim nowname? = divAsInt(clock(), 1000)value or expression? ' variable definition6
Console.WriteLine(nowarguments?) ' print7
Console.WriteLine(getDate(now)arguments?) ' print8
Else9
+Try
10
Dim tdname? = int(reply)value or expression? ' variable definition11
+If td >= 0condition? Then
12
printprocedureName?(getDate(td)arguments?)13
End If
Catch evariableName? As ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError?14
End Try
End If
End While
End Sub
public class Global {
+static void main() {
1
var replyname? = ""value or expression?;2
+while (!reply.upperCase().equals("Q")condition?) {
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression?; // reassign variable4
+if (reply.equals("")condition?) {
5
var nowname? = divAsInt(clock(), 1000)value or expression?;6
System.out.println(nowarguments?); // print7
System.out.println(getDate(now)arguments?); // print8
} else {9
+try {
10
var tdname? = int(reply)value or expression?;11
+if (td >= 0condition?) {
12
printprocedureName?(getDate(td)arguments?);13
} // if
} catch (ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? evariableName?) {14
} // try
} // if
} // while
} // main
}
From the Ripple sort demo, printing a list:
+main
1
variable liname? set to [7, 1, 0, 4, 8, 3, 6]value or expression?2
print(liarguments?)3
call inPlaceRippleSortprocedureName?(liarguments?)4
call printprocedureName?(liarguments?)5
end main
+def main() -> None:
1
liname? = [7, 1, 0, 4, 8, 3, 6]value or expression? # variable definition2
print(liarguments?)3
inPlaceRippleSortprocedureName?(liarguments?) # call procedure4
printprocedureName?(liarguments?)5
main()
+static void main() {
1
var liname? = [7, 1, 0, 4, 8, 3, 6]value or expression?;2
Console.WriteLine(liarguments?); // print3
inPlaceRippleSortprocedureName?(liarguments?); // call procedure4
printprocedureName?(liarguments?);5
} // main
+Sub main()
1
Dim liname? = {7, 1, 0, 4, 8, 3, 6}value or expression? ' variable definition2
Console.WriteLine(liarguments?) ' print3
inPlaceRippleSortprocedureName?(liarguments?) ' call procedure4
printprocedureName?(liarguments?)5
End Sub
public class Global {
+static void main() {
1
var liname? = [7, 1, 0, 4, 8, 3, 6]value or expression?;2
System.out.println(liarguments?); // print3
inPlaceRippleSortprocedureName?(liarguments?); // call procedure4
printprocedureName?(liarguments?);5
} // main
}
From the Snake - procedural demo, printing an interpolated string (last line of the main routine):
+main
1
variable blocksname? set to createBlockGraphics(white)value or expression?2
variable headname? set to [20, 15]value or expression?3
variable tailname? set to headvalue or expression?4
variable bodyname? set to [head]value or expression?5
variable currentDirname? set to Direction.rightvalue or expression?6
variable gameOnname? set to truevalue or expression?7
variable applename? set to [0, 0]value or expression?8
call setAppleToRandomPositionprocedureName?(apple, bodyarguments?)9
+while gameOncondition?
10
call updateDisplayprocedureName?(blocks, head, tail, body, applearguments?)11
variable currentDirRefname? set to new AsRef<of Direction>(currentDir)value or expression?12
variable headRefname? set to new AsRef<of List<of Int>>(head)value or expression?13
variable tailRefname? set to new AsRef<of List<of Int>>(tail)value or expression?14
call updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?)15
reassign headvariableName? to headRef.value()value or expression?16
reassign tailvariableName? to tailRef.value()value or expression?17
reassign currentDirvariableName? to currentDirRef.value()value or expression?18
reassign gameOnvariableName? to not hasHitEdge(head[0], head[1]) and not body.contains(head)value or expression?19
+if head.equals(apple)condition? then
20
call setAppleToRandomPositionprocedureName?(apple, bodyarguments?)21
else22
call body.removeAtprocedureName?(0arguments?)23
end if
call sleep_msprocedureName?(150arguments?)24
end while
call printprocedureName?($"Game Over! Score: {body.length() - 1}"arguments?)25
end main
+def main() -> None:
1
blocksname? = createBlockGraphics(white)value or expression? # variable definition2
headname? = [20, 15]value or expression? # variable definition3
tailname? = headvalue or expression? # variable definition4
bodyname? = [head]value or expression? # variable definition5
currentDirname? = Direction.rightvalue or expression? # variable definition6
gameOnname? = Truevalue or expression? # variable definition7
applename? = [0, 0]value or expression? # variable definition8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) # call procedure9
+while gameOncondition?:
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?) # call procedure11
currentDirRefname? = AsRef[Direction](currentDir)value or expression? # variable definition12
headRefname? = AsRef[list[int]](head)value or expression? # variable definition13
tailRefname? = AsRef[list[int]](tail)value or expression? # variable definition14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?) # call procedure15
headvariableName? = headRef.value()value or expression? # reassign variable16
tailvariableName? = tailRef.value()value or expression? # reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression? # reassign variable18
gameOnvariableName? = not hasHitEdge(head[0], head[1]) and not body.contains(head)value or expression? # reassign variable19
+if head.equals(apple)condition?:
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) # call procedure21
else:22
body.removeAtprocedureName?(0arguments?) # call procedure23
sleep_msprocedureName?(150arguments?) # call procedure24
printprocedureName?(f"Game Over! Score: {body.length() - 1}"arguments?)25
main()
+static void main() {
1
var blocksname? = createBlockGraphics(white)value or expression?;2
var headname? = [20, 15]value or expression?;3
var tailname? = headvalue or expression?;4
var bodyname? = [head]value or expression?;5
var currentDirname? = Direction.rightvalue or expression?;6
var gameOnname? = truevalue or expression?;7
var applename? = [0, 0]value or expression?;8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure9
+while (gameOncondition?) {
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?); // call procedure11
var currentDirRefname? = new AsRef<Direction>(currentDir)value or expression?;12
var headRefname? = new AsRef<List<int>>(head)value or expression?;13
var tailRefname? = new AsRef<List<int>>(tail)value or expression?;14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?); // call procedure15
headvariableName? = headRef.value()value or expression?; // reassign variable16
tailvariableName? = tailRef.value()value or expression?; // reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression?; // reassign variable18
gameOnvariableName? = !hasHitEdge(head[0], head[1]) && !body.contains(head)value or expression?; // reassign variable19
+if (head.equals(apple)condition?) {
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure21
} else {22
body.removeAtprocedureName?(0arguments?); // call procedure23
} // if
sleep_msprocedureName?(150arguments?); // call procedure24
} // while
printprocedureName?($"Game Over! Score: {body.length() - 1}"arguments?);25
} // main
+Sub main()
1
Dim blocksname? = createBlockGraphics(white)value or expression? ' variable definition2
Dim headname? = {20, 15}value or expression? ' variable definition3
Dim tailname? = headvalue or expression? ' variable definition4
Dim bodyname? = {head}value or expression? ' variable definition5
Dim currentDirname? = Direction.rightvalue or expression? ' variable definition6
Dim gameOnname? = Truevalue or expression? ' variable definition7
Dim applename? = {0, 0}value or expression? ' variable definition8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) ' call procedure9
+While gameOncondition?
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?) ' call procedure11
Dim currentDirRefname? = New AsRef(Of Direction)(currentDir)value or expression? ' variable definition12
Dim headRefname? = New AsRef(Of List(Of Integer))(head)value or expression? ' variable definition13
Dim tailRefname? = New AsRef(Of List(Of Integer))(tail)value or expression? ' variable definition14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?) ' call procedure15
headvariableName? = headRef.value()value or expression? ' reassign variable16
tailvariableName? = tailRef.value()value or expression? ' reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression? ' reassign variable18
gameOnvariableName? = Not hasHitEdge(head[0], head[1]) And Not body.contains(head)value or expression? ' reassign variable19
+If head.equals(apple)condition? Then
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) ' call procedure21
Else22
body.removeAtprocedureName?(0arguments?) ' call procedure23
End If
sleep_msprocedureName?(150arguments?) ' call procedure24
End While
printprocedureName?($"Game Over! Score: {body.length() - 1}"arguments?)25
End Sub
public class Global {
+static void main() {
1
var blocksname? = createBlockGraphics(white)value or expression?;2
var headname? = [20, 15]value or expression?;3
var tailname? = headvalue or expression?;4
var bodyname? = [head]value or expression?;5
var currentDirname? = Direction.rightvalue or expression?;6
var gameOnname? = truevalue or expression?;7
var applename? = [0, 0]value or expression?;8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure9
+while (gameOncondition?) {
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?); // call procedure11
var currentDirRefname? = new AsRef<Direction>(currentDir)value or expression?;12
var headRefname? = new AsRef<List<int>>(head)value or expression?;13
var tailRefname? = new AsRef<List<int>>(tail)value or expression?;14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?); // call procedure15
headvariableName? = headRef.value()value or expression?; // reassign variable16
tailvariableName? = tailRef.value()value or expression?; // reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression?; // reassign variable18
gameOnvariableName? = !hasHitEdge(head[0], head[1]) && !body.contains(head)value or expression?; // reassign variable19
+if (head.equals(apple)condition?) {
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure21
} else {22
body.removeAtprocedureName?(0arguments?); // call procedure23
} // if
sleep_msprocedureName?(150arguments?); // call procedure24
} // while
printprocedureName?(String.format("Game Over! Score: %", body.length() - 1)arguments?);25
} // main
}
What you need to know about print statements
What you need to know about print statements
- The print instruction writes a value to display.
- It automatically prints a new line after the value.
- The value can be any expression.
- You must provide a value; if you just want to print a blank line, just set the value to
new codenew codenew codenew codepublic class Global {
new code
}
+main
1
variable blocksname? set to createBlockGraphics(white)value or expression?2
variable headname? set to [20, 15]value or expression?3
variable tailname? set to headvalue or expression?4
variable bodyname? set to [head]value or expression?5
variable currentDirname? set to Direction.rightvalue or expression?6
variable gameOnname? set to truevalue or expression?7
variable applename? set to [0, 0]value or expression?8
call setAppleToRandomPositionprocedureName?(apple, bodyarguments?)9
+while gameOncondition?
10
call updateDisplayprocedureName?(blocks, head, tail, body, applearguments?)11
variable currentDirRefname? set to new AsRef<of Direction>(currentDir)value or expression?12
variable headRefname? set to new AsRef<of List<of Int>>(head)value or expression?13
variable tailRefname? set to new AsRef<of List<of Int>>(tail)value or expression?14
call updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?)15
reassign headvariableName? to headRef.value()value or expression?16
reassign tailvariableName? to tailRef.value()value or expression?17
reassign currentDirvariableName? to currentDirRef.value()value or expression?18
reassign gameOnvariableName? to not hasHitEdge(head[0], head[1]) and not body.contains(head)value or expression?19
+if head.equals(apple)condition? then
20
call setAppleToRandomPositionprocedureName?(apple, bodyarguments?)21
else22
call body.removeAtprocedureName?(0arguments?)23
end if
call sleep_msprocedureName?(150arguments?)24
end while
call printprocedureName?($"Game Over! Score: {body.length() - 1}"arguments?)25
end main
+def main() -> None:
1
blocksname? = createBlockGraphics(white)value or expression? # variable definition2
headname? = [20, 15]value or expression? # variable definition3
tailname? = headvalue or expression? # variable definition4
bodyname? = [head]value or expression? # variable definition5
currentDirname? = Direction.rightvalue or expression? # variable definition6
gameOnname? = Truevalue or expression? # variable definition7
applename? = [0, 0]value or expression? # variable definition8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) # call procedure9
+while gameOncondition?:
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?) # call procedure11
currentDirRefname? = AsRef[Direction](currentDir)value or expression? # variable definition12
headRefname? = AsRef[list[int]](head)value or expression? # variable definition13
tailRefname? = AsRef[list[int]](tail)value or expression? # variable definition14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?) # call procedure15
headvariableName? = headRef.value()value or expression? # reassign variable16
tailvariableName? = tailRef.value()value or expression? # reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression? # reassign variable18
gameOnvariableName? = not hasHitEdge(head[0], head[1]) and not body.contains(head)value or expression? # reassign variable19
+if head.equals(apple)condition?:
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) # call procedure21
else:22
body.removeAtprocedureName?(0arguments?) # call procedure23
sleep_msprocedureName?(150arguments?) # call procedure24
printprocedureName?(f"Game Over! Score: {body.length() - 1}"arguments?)25
main()
+static void main() {
1
var blocksname? = createBlockGraphics(white)value or expression?;2
var headname? = [20, 15]value or expression?;3
var tailname? = headvalue or expression?;4
var bodyname? = [head]value or expression?;5
var currentDirname? = Direction.rightvalue or expression?;6
var gameOnname? = truevalue or expression?;7
var applename? = [0, 0]value or expression?;8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure9
+while (gameOncondition?) {
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?); // call procedure11
var currentDirRefname? = new AsRef<Direction>(currentDir)value or expression?;12
var headRefname? = new AsRef<List<int>>(head)value or expression?;13
var tailRefname? = new AsRef<List<int>>(tail)value or expression?;14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?); // call procedure15
headvariableName? = headRef.value()value or expression?; // reassign variable16
tailvariableName? = tailRef.value()value or expression?; // reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression?; // reassign variable18
gameOnvariableName? = !hasHitEdge(head[0], head[1]) && !body.contains(head)value or expression?; // reassign variable19
+if (head.equals(apple)condition?) {
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure21
} else {22
body.removeAtprocedureName?(0arguments?); // call procedure23
} // if
sleep_msprocedureName?(150arguments?); // call procedure24
} // while
printprocedureName?($"Game Over! Score: {body.length() - 1}"arguments?);25
} // main
+Sub main()
1
Dim blocksname? = createBlockGraphics(white)value or expression? ' variable definition2
Dim headname? = {20, 15}value or expression? ' variable definition3
Dim tailname? = headvalue or expression? ' variable definition4
Dim bodyname? = {head}value or expression? ' variable definition5
Dim currentDirname? = Direction.rightvalue or expression? ' variable definition6
Dim gameOnname? = Truevalue or expression? ' variable definition7
Dim applename? = {0, 0}value or expression? ' variable definition8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) ' call procedure9
+While gameOncondition?
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?) ' call procedure11
Dim currentDirRefname? = New AsRef(Of Direction)(currentDir)value or expression? ' variable definition12
Dim headRefname? = New AsRef(Of List(Of Integer))(head)value or expression? ' variable definition13
Dim tailRefname? = New AsRef(Of List(Of Integer))(tail)value or expression? ' variable definition14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?) ' call procedure15
headvariableName? = headRef.value()value or expression? ' reassign variable16
tailvariableName? = tailRef.value()value or expression? ' reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression? ' reassign variable18
gameOnvariableName? = Not hasHitEdge(head[0], head[1]) And Not body.contains(head)value or expression? ' reassign variable19
+If head.equals(apple)condition? Then
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?) ' call procedure21
Else22
body.removeAtprocedureName?(0arguments?) ' call procedure23
End If
sleep_msprocedureName?(150arguments?) ' call procedure24
End While
printprocedureName?($"Game Over! Score: {body.length() - 1}"arguments?)25
End Sub
public class Global {
+static void main() {
1
var blocksname? = createBlockGraphics(white)value or expression?;2
var headname? = [20, 15]value or expression?;3
var tailname? = headvalue or expression?;4
var bodyname? = [head]value or expression?;5
var currentDirname? = Direction.rightvalue or expression?;6
var gameOnname? = truevalue or expression?;7
var applename? = [0, 0]value or expression?;8
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure9
+while (gameOncondition?) {
10
updateDisplayprocedureName?(blocks, head, tail, body, applearguments?); // call procedure11
var currentDirRefname? = new AsRef<Direction>(currentDir)value or expression?;12
var headRefname? = new AsRef<List<int>>(head)value or expression?;13
var tailRefname? = new AsRef<List<int>>(tail)value or expression?;14
updateSnakeprocedureName?(currentDirRef, tailRef, headRef, bodyarguments?); // call procedure15
headvariableName? = headRef.value()value or expression?; // reassign variable16
tailvariableName? = tailRef.value()value or expression?; // reassign variable17
currentDirvariableName? = currentDirRef.value()value or expression?; // reassign variable18
gameOnvariableName? = !hasHitEdge(head[0], head[1]) && !body.contains(head)value or expression?; // reassign variable19
+if (head.equals(apple)condition?) {
20
setAppleToRandomPositionprocedureName?(apple, bodyarguments?); // call procedure21
} else {22
body.removeAtprocedureName?(0arguments?); // call procedure23
} // if
sleep_msprocedureName?(150arguments?); // call procedure24
} // while
printprocedureName?(String.format("Game Over! Score: %", body.length() - 1)arguments?);25
} // main
}
What you need to know about print statements
- The print instruction writes a value to display.
- It automatically prints a new line after the value.
- The value can be any expression.
- You must provide a value; if you just want to print a blank line, just set the value to
"""""""""".
- The recommended way to print multiple values is to use an interpolated string (see third example above)
More advanced techniques
As an alternative to using the print instruction you call one of these standard library procedures Using
the call procedure instruction:
which offer more flexible options. These library methods work when running any of the supported languages within Elan but,
unlike when using the normal the print instruction, the library procedures do not correspond directly to
print methods in the native libraries for those languages.
Variable definition
Examples of variable definitions
From the Burrow demo, where variables named xxxxx and yyyyy represent the current
coordinates for the burrowing 'animal':
variable xname? set to 20value or expression?0
xname? = 20value or expression? # variable definition0
var xname? = 20value or expression?;0
Dim xname? = 20value or expression? ' variable definition0
var xname? = 20value or expression?;0
variable yname? set to 15value or expression?0
yname? = 15value or expression? # variable definition0
var yname? = 15value or expression?;0
Dim yname? = 15value or expression? ' variable definition0
var yname? = 15value or expression?;0
and the values of these variables are used in multiple places within the program, for example here:
reassign blocks[x][y]variableName? to redvalue or expression?0
blocks[x][y]variableName? = redvalue or expression? # reassign variable0
blocks[x][y]variableName? = redvalue or expression?; // reassign variable0
blocks[x][y]variableName? = redvalue or expression? ' reassign variable0
blocks[x][y]variableName? = redvalue or expression?; // reassign variable0
From the Life - procedural demo where a variable is initialised using an expression, in this
case a function call:
variable gridname? set to createBlockGraphics(white)value or expression?0
gridname? = createBlockGraphics(white)value or expression? # variable definition0
var gridname? = createBlockGraphics(white)value or expression?;0
Dim gridname? = createBlockGraphics(white)value or expression? ' variable definition0
var gridname? = createBlockGraphics(white)value or expression?;0
Example from the Ripple Sort demo where a variable being initialised using a literal llist of integers:
variable liname? set to [7, 1, 0, 4, 8, 3, 6]value or expression?0
liname? = [7, 1, 0, 4, 8, 3, 6]value or expression? # variable definition0
var liname? = [7, 1, 0, 4, 8, 3, 6]value or expression?;0
Dim liname? = {7, 1, 0, 4, 8, 3, 6}value or expression? ' variable definition0
var liname? = [7, 1, 0, 4, 8, 3, 6]value or expression?;0
What you need to know about variable definitions
- A variable is a named value, were the value may be changed during the course of running the program.
- The variable definition instruction allows you to define a new variable.
- The instructions require a name (e.g.
xxxxx) and an initial value.
- The initial value may be specified using a literal value (such as
2020202020, or "apple""apple""apple""apple""apple")
or by an expression.
- The type of the initial value determines the type of the variable, so if/when the variable is later
changed to a new value, that new value must be of that same type./li>
- Variables may only be defined within a method (the main routine, a procedure, or function).
- Elan does not permit 'global variables' to be defined - even though, outside Elan, that would be
allowed by all the languages - because it is considered a bad practice.
Reassign variable
Examples of variable reassignment
From the Ripple sort demo, where the hasChangedhasChangedhasChangedhasChangedhasChanged variable, having been defined higher up,
is reassigned to falseFalsefalseFalsefalse at the start of the while loop,
and then to trueTruetrueTruetrue if two values are swapped:
+procedure inPlaceRippleSortname?(li as List<of Int>parameter definitions?)
1
variable hasChangedname? set to truevalue or expression?2
variable lastCompname? set to li.length() - 2value or expression?3
+while hasChanged is truecondition?
4
reassign hasChangedvariableName? to falsevalue or expression?5
+for iitem? in range(0, lastComp + 1)source?
6
+if li[i] > li[i + 1]condition? then
7
variable tempname? set to li[i]value or expression?8
reassign li[i]variableName? to li[i + 1]value or expression?9
reassign li[i + 1]variableName? to tempvalue or expression?10
reassign hasChangedvariableName? to truevalue or expression?11
end if
end for
reassign lastCompvariableName? to lastComp - 1value or expression?12
end while
end procedure
+def inPlaceRippleSortname?(li: list[int]parameter definitions?) -> None:
# procedure1
hasChangedname? = Truevalue or expression? # variable definition2
lastCompname? = li.length() - 2value or expression? # variable definition3
+while hasChanged == Truecondition?:
4
hasChangedvariableName? = Falsevalue or expression? # reassign variable5
+for iitem? in range(0, lastComp + 1)source?:
6
+if li[i] > li[i + 1]condition?:
7
tempname? = li[i]value or expression? # variable definition8
li[i]variableName? = li[i + 1]value or expression? # reassign variable9
li[i + 1]variableName? = tempvalue or expression? # reassign variable10
hasChangedvariableName? = Truevalue or expression? # reassign variable11
lastCompvariableName? = lastComp - 1value or expression? # reassign variable12
+static void inPlaceRippleSortname?(List<int> liparameter definitions?) {
// procedure1
var hasChangedname? = truevalue or expression?;2
var lastCompname? = li.length() - 2value or expression?;3
+while (hasChanged == truecondition?) {
4
hasChangedvariableName? = falsevalue or expression?; // reassign variable5
+foreach (iitem? in range(0, lastComp + 1)source?) {
6
+if (li[i] > li[i + 1]condition?) {
7
var tempname? = li[i]value or expression?;8
li[i]variableName? = li[i + 1]value or expression?; // reassign variable9
li[i + 1]variableName? = tempvalue or expression?; // reassign variable10
hasChangedvariableName? = truevalue or expression?; // reassign variable11
} // if
} // foreach
lastCompvariableName? = lastComp - 1value or expression?; // reassign variable12
} // while
} // procedure
+Sub inPlaceRippleSortname?(li As List(Of Integer)parameter definitions?)
' procedure1
Dim hasChangedname? = Truevalue or expression? ' variable definition2
Dim lastCompname? = li.length() - 2value or expression? ' variable definition3
+While hasChanged = Truecondition?
4
hasChangedvariableName? = Falsevalue or expression? ' reassign variable5
+For Each iitem? In range(0, lastComp + 1)source?
6
+If li[i] > li[i + 1]condition? Then
7
Dim tempname? = li[i]value or expression? ' variable definition8
li[i]variableName? = li[i + 1]value or expression? ' reassign variable9
li[i + 1]variableName? = tempvalue or expression? ' reassign variable10
hasChangedvariableName? = Truevalue or expression? ' reassign variable11
End If
Next i
lastCompvariableName? = lastComp - 1value or expression? ' reassign variable12
End While
End Sub
public class Global {
+static void inPlaceRippleSortname?(List<int> liparameter definitions?) {
// procedure1
var hasChangedname? = truevalue or expression?;2
var lastCompname? = li.length() - 2value or expression?;3
+while (hasChanged == truecondition?) {
4
hasChangedvariableName? = falsevalue or expression?; // reassign variable5
+foreach (iitem? in range(0, lastComp + 1)source?) {
6
+if (li[i] > li[i + 1]condition?) {
7
var tempname? = li[i]value or expression?;8
li[i]variableName? = li[i + 1]value or expression?; // reassign variable9
li[i + 1]variableName? = tempvalue or expression?; // reassign variable10
hasChangedvariableName? = truevalue or expression?; // reassign variable11
} // if
} // foreach
lastCompvariableName? = lastComp - 1value or expression?; // reassign variable12
} // while
} // procedure
}
From the Snake -procedural demo, where the variable currentDefcurrentDefcurrentDefcurrentDefcurrentDef is reassigned to a new value by calling the function
directionByKey, which is passed a keypress that has been read from the keyboard using the getKey
system method:
+procedure updateSnakename?(currentDirRef as AsRef<of Direction>, tailRef as AsRef<of List<of Int>>, headRef as AsRef<of List<of Int>>, body as List<of List<of Int>>parameter definitions?)
1
variable headname? set to headRef.value()value or expression?2
variable tailname? set to tailRef.value()value or expression?3
variable currentDirname? set to currentDirRef.value()value or expression?4
reassign currentDirvariableName? to directionByKey(currentDir, getKey())value or expression?5
call tailRef.setprocedureName?(body[0]arguments?)6
call body.appendprocedureName?(headarguments?)7
call headRef.setprocedureName?(getAdjacentSquare(head, currentDir)arguments?)8
call currentDirRef.setprocedureName?(currentDirarguments?)9
end procedure
+def updateSnakename?(currentDirRef: AsRef[Direction], tailRef: AsRef[list[int]], headRef: AsRef[list[int]], body: list[list[int]]parameter definitions?) -> None:
# procedure1
headname? = headRef.value()value or expression? # variable definition2
tailname? = tailRef.value()value or expression? # variable definition3
currentDirname? = currentDirRef.value()value or expression? # variable definition4
currentDirvariableName? = directionByKey(currentDir, getKey())value or expression? # reassign variable5
tailRef.setprocedureName?(body[0]arguments?) # call procedure6
body.appendprocedureName?(headarguments?) # call procedure7
headRef.setprocedureName?(getAdjacentSquare(head, currentDir)arguments?) # call procedure8
currentDirRef.setprocedureName?(currentDirarguments?) # call procedure9
+static void updateSnakename?(AsRef<Direction> currentDirRef, AsRef<List<int>> tailRef, AsRef<List<int>> headRef, List<List<int>> bodyparameter definitions?) {
// procedure1
var headname? = headRef.value()value or expression?;2
var tailname? = tailRef.value()value or expression?;3
var currentDirname? = currentDirRef.value()value or expression?;4
currentDirvariableName? = directionByKey(currentDir, getKey())value or expression?; // reassign variable5
tailRef.setprocedureName?(body[0]arguments?); // call procedure6
body.appendprocedureName?(headarguments?); // call procedure7
headRef.setprocedureName?(getAdjacentSquare(head, currentDir)arguments?); // call procedure8
currentDirRef.setprocedureName?(currentDirarguments?); // call procedure9
} // procedure
+Sub updateSnakename?(currentDirRef As AsRef(Of Direction), tailRef As AsRef(Of List(Of Integer)), headRef As AsRef(Of List(Of Integer)), body As List(Of List(Of Integer))parameter definitions?)
' procedure1
Dim headname? = headRef.value()value or expression? ' variable definition2
Dim tailname? = tailRef.value()value or expression? ' variable definition3
Dim currentDirname? = currentDirRef.value()value or expression? ' variable definition4
currentDirvariableName? = directionByKey(currentDir, getKey())value or expression? ' reassign variable5
tailRef.setprocedureName?(body[0]arguments?) ' call procedure6
body.appendprocedureName?(headarguments?) ' call procedure7
headRef.setprocedureName?(getAdjacentSquare(head, currentDir)arguments?) ' call procedure8
currentDirRef.setprocedureName?(currentDirarguments?) ' call procedure9
End Sub
public class Global {
+static void updateSnakename?(AsRef<Direction> currentDirRef, AsRef<List<int>> tailRef, AsRef<List<int>> headRef, List<List<int>> bodyparameter definitions?) {
// procedure1
var headname? = headRef.value()value or expression?;2
var tailname? = tailRef.value()value or expression?;3
var currentDirname? = currentDirRef.value()value or expression?;4
currentDirvariableName? = directionByKey(currentDir, getKey())value or expression?; // reassign variable5
tailRef.setprocedureName?(body[0]arguments?); // call procedure6
body.appendprocedureName?(headarguments?); // call procedure7
headRef.setprocedureName?(getAdjacentSquare(head, currentDir)arguments?); // call procedure8
currentDirRef.setprocedureName?(currentDirarguments?); // call procedure9
} // procedure
}
What you need to know about reassigning a variable
- The reassign variable instruction assigns a new value to a variable that has been defined earlier within the method.
- The new value to be assigned may be defined as a literal value, or as the result of evaluating an expression.
- The new value must be of the same type as the one that the variable was defined with.
If statement
Examples of if statements
From the Life - procedural demo, where the resultresultresultresultresult is changed from black to white if a given
random value (expected to be in the range 0 to 1) is greater than 0.5:
+function blackOrWhitename?(random as Floatparameter definitions?) returns IntType?
1
variable resultname? set to blackvalue or expression?2
+if random > 0.5condition? then
3
reassign resultvariableName? to whitevalue or expression?4
end if
return resultvalue or expression?5
end function
+def blackOrWhitename?(random: floatparameter definitions?) -> intType?:
# function1
resultname? = blackvalue or expression? # variable definition2
+if random > 0.5condition?:
3
resultvariableName? = whitevalue or expression? # reassign variable4
return resultvalue or expression?5
+static intType? blackOrWhitename?(double randomparameter definitions?) {
// function1
var resultname? = blackvalue or expression?;2
+if (random > 0.5condition?) {
3
resultvariableName? = whitevalue or expression?; // reassign variable4
} // if
return resultvalue or expression?;5
} // function
+Function blackOrWhitename?(random As Doubleparameter definitions?) As IntegerType?
1
Dim resultname? = blackvalue or expression? ' variable definition2
+If random > 0.5condition? Then
3
resultvariableName? = whitevalue or expression? ' reassign variable4
End If
Return resultvalue or expression?5
End Function
public class Global {
+static intType? blackOrWhitename?(double randomparameter definitions?) {
// function1
var resultname? = blackvalue or expression?;2
+if (random > 0.5condition?) {
3
resultvariableName? = whitevalue or expression?; // reassign variable4
} // if
return resultvalue or expression?;5
} // function
}
From the Bubbles demo where the if statement includes an else clause,
so that one of two separate blocks of statements is always executed depending on the value of a randomly-generated number.
+if random() < 0.05condition? then
0
#
call b.setRadiusprocedureName?(0arguments?)1
call b.setCentreYprocedureName?(75arguments?)2
else3
#
call b.setCentreYprocedureName?(b.centreY - 1arguments?)4
call b.setRadiusprocedureName?(b.radius + 0.2arguments?)5
end if
+if random() < 0.05condition?:
0
#
b.setRadiusprocedureName?(0arguments?) # call procedure1
b.setCentreYprocedureName?(75arguments?) # call procedure2
else:3
#
b.setCentreYprocedureName?(b.centreY - 1arguments?) # call procedure4
b.setRadiusprocedureName?(b.radius + 0.2arguments?) # call procedure5
+if (random() < 0.05condition?) {
0
//
b.setRadiusprocedureName?(0arguments?); // call procedure1
b.setCentreYprocedureName?(75arguments?); // call procedure2
} else {3
//
b.setCentreYprocedureName?(b.centreY - 1arguments?); // call procedure4
b.setRadiusprocedureName?(b.radius + 0.2arguments?); // call procedure5
} // if
+If random() < 0.05condition? Then
0
'
b.setRadiusprocedureName?(0arguments?) ' call procedure1
b.setCentreYprocedureName?(75arguments?) ' call procedure2
Else3
'
b.setCentreYprocedureName?(b.centreY - 1arguments?) ' call procedure4
b.setRadiusprocedureName?(b.radius + 0.2arguments?) ' call procedure5
End If
+if (random() < 0.05condition?) {
0
//
b.setRadiusprocedureName?(0arguments?); // call procedure1
b.setCentreYprocedureName?(75arguments?); // call procedure2
} else {3
//
b.setCentreYprocedureName?(b.centreY - 1arguments?); // call procedure4
b.setRadiusprocedureName?(b.radius + 0.2arguments?); // call procedure5
} // if
From the Burrow demo, where the if includes multiple else if (shortened to 'elif' in some languages) clauses, such that depending
on the value of the directiondirectiondirectiondirectiondirection variable, one of four different statements is executed:
+if direction is 0condition? then
0
reassign xvariableName? to min([x + 1, 39])value or expression?1
elif direction is 1condition? then2
reassign xvariableName? to max([x - 1, 0])value or expression?3
elif direction is 2condition? then4
reassign yvariableName? to min([y + 1, 29])value or expression?5
elif direction is 3condition? then6
reassign yvariableName? to max([y - 1, 0])value or expression?7
end if
+if direction == 0condition?:
0
xvariableName? = min([x + 1, 39])value or expression? # reassign variable1
elif direction == 1condition?: # else if2
xvariableName? = max([x - 1, 0])value or expression? # reassign variable3
elif direction == 2condition?: # else if4
yvariableName? = min([y + 1, 29])value or expression? # reassign variable5
elif direction == 3condition?: # else if6
yvariableName? = max([y - 1, 0])value or expression? # reassign variable7
+if (direction == 0condition?) {
0
xvariableName? = min([x + 1, 39])value or expression?; // reassign variable1
} else if (direction == 1condition?) {2
xvariableName? = max([x - 1, 0])value or expression?; // reassign variable3
} else if (direction == 2condition?) {4
yvariableName? = min([y + 1, 29])value or expression?; // reassign variable5
} else if (direction == 3condition?) {6
yvariableName? = max([y - 1, 0])value or expression?; // reassign variable7
} // if
+If direction = 0condition? Then
0
xvariableName? = min({x + 1, 39})value or expression? ' reassign variable1
ElseIf direction = 1condition? Then2
xvariableName? = max({x - 1, 0})value or expression? ' reassign variable3
ElseIf direction = 2condition? Then4
yvariableName? = min({y + 1, 29})value or expression? ' reassign variable5
ElseIf direction = 3condition? Then6
yvariableName? = max({y - 1, 0})value or expression? ' reassign variable7
End If
+if (direction == 0condition?) {
0
xvariableName? = min([x + 1, 39])value or expression?; // reassign variable1
} else if (direction == 1condition?) {2
xvariableName? = max([x - 1, 0])value or expression?; // reassign variable3
} else if (direction == 2condition?) {4
yvariableName? = min([y + 1, 29])value or expression?; // reassign variable5
} else if (direction == 3condition?) {6
yvariableName? = max([y - 1, 0])value or expression?; // reassign variable7
} // if
(Note: the last else if clause could have been written as an else clause,
but writing it as an else if makes the intention clearer to a human reader).
What you need to know about an if statement
- The if instruction specifies which of several blocks of instructions is to be executed next.
- It requires a condition to be specified, which must evaluate to a Boolean value -
trueTruetrueTruetrue or falseFalsefalseFalsefalse.
- That expression might take any of the following forms:
- comparing two values using one of the Boolean operators e.g.
random() < 0.05random() < 0.05random() < 0.05random() < 0.05random() < 0.05 or direction is 0direction == 0direction == 0direction = 0direction == 0
- a single existing variable that is of Boolean type
- a function call, or any more complex expression, that evaluates to a Boolean value
- Within the body of an if expression you may add one or more else if clauses and/or a single else clause
- If you specify an else clause it must be the final clause within the if statement body
While loop
Examples of while loops
From the Ripple Sort demo, where the loop is executed until a pass right through the loop has left the hasChangedhasChangedhasChangedhasChangedhasChanged
variable as false (meaning that no more swaps have occurred):
Code does not parse as Elan.false (meaning that no more swaps have occurred):
Code does not parse as Elan.false (meaning that no more swaps have occurred):
Code does not parse as Elan.false (meaning that no more swaps have occurred):
Code does not parse as Elan.false (meaning that no more swaps have occurred):
Code does not parse as Elan.
+while hasChanged is truecondition?
0
reassign hasChangedvariableName? to falsevalue or expression?1
+for iitem? in range(0, lastComp + 1)source?
2
+if li[i] > li[i + 1]condition? then
3
variable tempname? set to li[i]value or expression?4
reassign li[i]variableName? to li[i + 1]value or expression?5
reassign li[i + 1]variableName? to tempvalue or expression?6
reassign hasChangedvariableName? to truevalue or expression?7
end if
end for
reassign lastCompvariableName? to lastComp - 1value or expression?8
end while
+while hasChanged == Truecondition?:
0
hasChangedvariableName? = Falsevalue or expression? # reassign variable1
+for iitem? in range(0, lastComp + 1)source?:
2
+if li[i] > li[i + 1]condition?:
3
tempname? = li[i]value or expression? # variable definition4
li[i]variableName? = li[i + 1]value or expression? # reassign variable5
li[i + 1]variableName? = tempvalue or expression? # reassign variable6
hasChangedvariableName? = Truevalue or expression? # reassign variable7
lastCompvariableName? = lastComp - 1value or expression? # reassign variable8
+while (hasChanged == truecondition?) {
0
hasChangedvariableName? = falsevalue or expression?; // reassign variable1
+foreach (iitem? in range(0, lastComp + 1)source?) {
2
+if (li[i] > li[i + 1]condition?) {
3
var tempname? = li[i]value or expression?;4
li[i]variableName? = li[i + 1]value or expression?; // reassign variable5
li[i + 1]variableName? = tempvalue or expression?; // reassign variable6
hasChangedvariableName? = truevalue or expression?; // reassign variable7
} // if
} // foreach
lastCompvariableName? = lastComp - 1value or expression?; // reassign variable8
} // while
+While hasChanged = Truecondition?
0
hasChangedvariableName? = Falsevalue or expression? ' reassign variable1
+For Each iitem? In range(0, lastComp + 1)source?
2
+If li[i] > li[i + 1]condition? Then
3
Dim tempname? = li[i]value or expression? ' variable definition4
li[i]variableName? = li[i + 1]value or expression? ' reassign variable5
li[i + 1]variableName? = tempvalue or expression? ' reassign variable6
hasChangedvariableName? = Truevalue or expression? ' reassign variable7
End If
Next i
lastCompvariableName? = lastComp - 1value or expression? ' reassign variable8
End While
+while (hasChanged == truecondition?) {
0
hasChangedvariableName? = falsevalue or expression?; // reassign variable1
+foreach (iitem? in range(0, lastComp + 1)source?) {
2
+if (li[i] > li[i + 1]condition?) {
3
var tempname? = li[i]value or expression?;4
li[i]variableName? = li[i + 1]value or expression?; // reassign variable5
li[i + 1]variableName? = tempvalue or expression?; // reassign variable6
hasChangedvariableName? = truevalue or expression?; // reassign variable7
} // if
} // foreach
lastCompvariableName? = lastComp - 1value or expression?; // reassign variable8
} // while
From the Bubbles demo, where the condition is 'hard-wired' to trueTruetrueTruetrue, which means that the loop will
executive indefinitely (until you stop the program manually with the stop button):
+while truecondition?
0
call moveGrowBurstprocedureName?(bubblesarguments?)1
end while
+while Truecondition?:
0
moveGrowBurstprocedureName?(bubblesarguments?) # call procedure1
+while (truecondition?) {
0
moveGrowBurstprocedureName?(bubblesarguments?); // call procedure1
} // while
+While Truecondition?
0
moveGrowBurstprocedureName?(bubblesarguments?) ' call procedure1
End While
+while (truecondition?) {
0
moveGrowBurstprocedureName?(bubblesarguments?); // call procedure1
} // while
From the Tower of Hanoi demo, which uses a more complicated expression - to exit the loop only when the third stack
has all the discs on it:
+while stacks[2].length() isnt nDiscscondition?
0
+if (stacks[0].length() mod 2) is 0condition? then
1
call moveBetweenprocedureName?(stacks, 0, 1arguments?)2
call moveBetweenprocedureName?(stacks, 0, 2arguments?)3
call moveBetweenprocedureName?(stacks, 1, 2arguments?)4
else5
call moveBetweenprocedureName?(stacks, 0, 2arguments?)6
call moveBetweenprocedureName?(stacks, 0, 1arguments?)7
call moveBetweenprocedureName?(stacks, 1, 2arguments?)8
end if
end while
+while stacks[2].length() != nDiscscondition?:
0
+if (stacks[0].length() % 2) == 0condition?:
1
moveBetweenprocedureName?(stacks, 0, 1arguments?) # call procedure2
moveBetweenprocedureName?(stacks, 0, 2arguments?) # call procedure3
moveBetweenprocedureName?(stacks, 1, 2arguments?) # call procedure4
else:5
moveBetweenprocedureName?(stacks, 0, 2arguments?) # call procedure6
moveBetweenprocedureName?(stacks, 0, 1arguments?) # call procedure7
moveBetweenprocedureName?(stacks, 1, 2arguments?) # call procedure8
+while (stacks[2].length() != nDiscscondition?) {
0
+if ((stacks[0].length() % 2) == 0condition?) {
1
moveBetweenprocedureName?(stacks, 0, 1arguments?); // call procedure2
moveBetweenprocedureName?(stacks, 0, 2arguments?); // call procedure3
moveBetweenprocedureName?(stacks, 1, 2arguments?); // call procedure4
} else {5
moveBetweenprocedureName?(stacks, 0, 2arguments?); // call procedure6
moveBetweenprocedureName?(stacks, 0, 1arguments?); // call procedure7
moveBetweenprocedureName?(stacks, 1, 2arguments?); // call procedure8
} // if
} // while
+While stacks[2].length() <> nDiscscondition?
0
+If (stacks[0].length() Mod 2) = 0condition? Then
1
moveBetweenprocedureName?(stacks, 0, 1arguments?) ' call procedure2
moveBetweenprocedureName?(stacks, 0, 2arguments?) ' call procedure3
moveBetweenprocedureName?(stacks, 1, 2arguments?) ' call procedure4
Else5
moveBetweenprocedureName?(stacks, 0, 2arguments?) ' call procedure6
moveBetweenprocedureName?(stacks, 0, 1arguments?) ' call procedure7
moveBetweenprocedureName?(stacks, 1, 2arguments?) ' call procedure8
End If
End While
+while (stacks[2].length() != nDiscscondition?) {
0
+if ((stacks[0].length() % 2) == 0condition?) {
1
moveBetweenprocedureName?(stacks, 0, 1arguments?); // call procedure2
moveBetweenprocedureName?(stacks, 0, 2arguments?); // call procedure3
moveBetweenprocedureName?(stacks, 1, 2arguments?); // call procedure4
} else {5
moveBetweenprocedureName?(stacks, 0, 2arguments?); // call procedure6
moveBetweenprocedureName?(stacks, 0, 1arguments?); // call procedure7
moveBetweenprocedureName?(stacks, 1, 2arguments?); // call procedure8
} // if
} // while
What you need to know about a while loop
- The while loop specifies that a block of statements is to be repeated (or 'iterated') for as long as a specified condition is true.
- It should be used when you which to repeat something, but you don't know, in advance, how many times you need it to be repeated.
- As on an if statement, the condition in a while loop may be any expression that evaluates to a Boolean value.
- The condition is evaluated at the beginning of each pass through the loop.
- If the condition makes use of one or more variables (most do) then the variable(s) must be defined before the start of the loop.
- It is possible that the condition could evaluate to false even on the first pass, in which case the instructions within the loop are
not executed at all.
- Conversely, if the condition always evaluates to true, then the loop will executive indefintely; this can be deliberate, but
may also be an accidental and unwanted behaviour caused by specifying the condition inaccurately.
For loop
Examples of for loops
From the Bubbles demo, where the loop is designed to execute exactly 20 times (the upped bound of the range
function being exclusive) to create 20 bubbles of the same size, but of different (randomised) colours, and spread evenly across
the bottom of the display:
+for iitem? in range(1, 21)source?
0
variable bname? set to (new CircleVG()).withCentreX(i*5 + 2).withCentreY(75).withRadius(0).withFillColour(transparent).withStrokeColour(randint(0, white))value or expression?1
call bubbles.appendprocedureName?(barguments?)2
end for
+for iitem? in range(1, 21)source?:
0
bname? = (CircleVG()).withCentreX(i*5 + 2).withCentreY(75).withRadius(0).withFillColour(transparent).withStrokeColour(randint(0, white))value or expression? # variable definition1
bubbles.appendprocedureName?(barguments?) # call procedure2
+foreach (iitem? in range(1, 21)source?) {
0
var bname? = (new CircleVG()).withCentreX(i*5 + 2).withCentreY(75).withRadius(0).withFillColour(transparent).withStrokeColour(randint(0, white))value or expression?;1
bubbles.appendprocedureName?(barguments?); // call procedure2
} // foreach
+For Each iitem? In range(1, 21)source?
0
Dim bname? = (New CircleVG()).withCentreX(i*5 + 2).withCentreY(75).withRadius(0).withFillColour(transparent).withStrokeColour(randint(0, white))value or expression? ' variable definition1
bubbles.appendprocedureName?(barguments?) ' call procedure2
Next i
+foreach (iitem? in range(1, 21)source?) {
0
var bname? = (new CircleVG()).withCentreX(i*5 + 2).withCentreY(75).withRadius(0).withFillColour(transparent).withStrokeColour(randint(0, white))value or expression?;1
bubbles.appendprocedureName?(barguments?); // call procedure2
} // foreach
From the Turtle Dragon demo, where the number of times to execute the loop is calculated - based on the number of characters in
the string variable named turnsturnsturnsturnsturns:
+procedure drawDragonname?(t as Turtle, order as Int, turns as String, side as Float, corner as Floatparameter definitions?)
1
variable pname? set to (200.0/order).floor()value or expression?2
variable turnIname? set to 0value or expression?3
+for turnitem? in turnssource?
4
reassign turnIvariableName? to (if(turn.equals(left), 1, -1))value or expression?5
call t.turnprocedureName?(-45*turnIarguments?)6
call t.moveprocedureName?(cornerarguments?)7
call t.turnprocedureName?(-45*turnIarguments?)8
call t.moveprocedureName?(sidearguments?)9
call sleep_msprocedureName?(parguments?)10
end for
call t.penUpprocedureName?(arguments?)11
call t.hideprocedureName?(arguments?)12
end procedure
+def drawDragonname?(t: Turtle, order: int, turns: str, side: float, corner: floatparameter definitions?) -> None:
# procedure1
pname? = (200.0/order).floor()value or expression? # variable definition2
turnIname? = 0value or expression? # variable definition3
+for turnitem? in turnssource?:
4
turnIvariableName? = (if(turn.equals(left), 1, -1))value or expression? # reassign variable5
t.turnprocedureName?(-45*turnIarguments?) # call procedure6
t.moveprocedureName?(cornerarguments?) # call procedure7
t.turnprocedureName?(-45*turnIarguments?) # call procedure8
t.moveprocedureName?(sidearguments?) # call procedure9
sleep_msprocedureName?(parguments?) # call procedure10
t.penUpprocedureName?(arguments?) # call procedure11
t.hideprocedureName?(arguments?) # call procedure12
+static void drawDragonname?(Turtle t, int order, string turns, double side, double cornerparameter definitions?) {
// procedure1
var pname? = (200.0/order).floor()value or expression?;2
var turnIname? = 0value or expression?;3
+foreach (turnitem? in turnssource?) {
4
turnIvariableName? = (if(turn.equals(left), 1, -1))value or expression?; // reassign variable5
t.turnprocedureName?(-45*turnIarguments?); // call procedure6
t.moveprocedureName?(cornerarguments?); // call procedure7
t.turnprocedureName?(-45*turnIarguments?); // call procedure8
t.moveprocedureName?(sidearguments?); // call procedure9
sleep_msprocedureName?(parguments?); // call procedure10
} // foreach
t.penUpprocedureName?(arguments?); // call procedure11
t.hideprocedureName?(arguments?); // call procedure12
} // procedure
+Sub drawDragonname?(t As Turtle, order As Integer, turns As String, side As Double, corner As Doubleparameter definitions?)
' procedure1
Dim pname? = (200.0/order).floor()value or expression? ' variable definition2
Dim turnIname? = 0value or expression? ' variable definition3
+For Each turnitem? In turnssource?
4
turnIvariableName? = (if(turn.equals(left), 1, -1))value or expression? ' reassign variable5
t.turnprocedureName?(-45*turnIarguments?) ' call procedure6
t.moveprocedureName?(cornerarguments?) ' call procedure7
t.turnprocedureName?(-45*turnIarguments?) ' call procedure8
t.moveprocedureName?(sidearguments?) ' call procedure9
sleep_msprocedureName?(parguments?) ' call procedure10
Next turn
t.penUpprocedureName?(arguments?) ' call procedure11
t.hideprocedureName?(arguments?) ' call procedure12
End Sub
public class Global {
+static void drawDragonname?(Turtle t, int order, String turns, double side, double cornerparameter definitions?) {
// procedure1
var pname? = (200.0/order).floor()value or expression?;2
var turnIname? = 0value or expression?;3
+foreach (turnitem? in turnssource?) {
4
turnIvariableName? = (if(turn.equals(left), 1, -1))value or expression?; // reassign variable5
t.turnprocedureName?(-45*turnIarguments?); // call procedure6
t.moveprocedureName?(cornerarguments?); // call procedure7
t.turnprocedureName?(-45*turnIarguments?); // call procedure8
t.moveprocedureName?(sidearguments?); // call procedure9
sleep_msprocedureName?(parguments?); // call procedure10
} // foreach
t.penUpprocedureName?(arguments?); // call procedure11
t.hideprocedureName?(arguments?); // call procedure12
} // procedure
}
From the Life - procedural demo, a 'nested' for loop (one inside another) to apply the two inner instructions to each
cell in the 40 x 30 grid:
+for xitem? in range(0, 40)source?
0
+for yitem? in range(0, 30)source?
1
variable colourname? set to nextCellValue(grid, x, y)value or expression?2
reassign nextGen[x][y]variableName? to colourvalue or expression?3
end for
end for
+for xitem? in range(0, 40)source?:
0
+for yitem? in range(0, 30)source?:
1
colourname? = nextCellValue(grid, x, y)value or expression? # variable definition2
nextGen[x][y]variableName? = colourvalue or expression? # reassign variable3
+foreach (xitem? in range(0, 40)source?) {
0
+foreach (yitem? in range(0, 30)source?) {
1
var colourname? = nextCellValue(grid, x, y)value or expression?;2
nextGen[x][y]variableName? = colourvalue or expression?; // reassign variable3
} // foreach
} // foreach
+For Each xitem? In range(0, 40)source?
0
+For Each yitem? In range(0, 30)source?
1
Dim colourname? = nextCellValue(grid, x, y)value or expression? ' variable definition2
nextGen[x][y]variableName? = colourvalue or expression? ' reassign variable3
Next y
Next x
+foreach (xitem? in range(0, 40)source?) {
0
+foreach (yitem? in range(0, 30)source?) {
1
var colourname? = nextCellValue(grid, x, y)value or expression?;2
nextGen[x][y]variableName? = colourvalue or expression?; // reassign variable3
} // foreach
} // foreach
What you need to know about for loops
- The Elan concept of a for loop is equivalent to a 'for each' loop in several languages.
- It should be used when you know (or can work out), in advance, how many times you need it to be repeated.
- The for loop specifies that a block of statements that is repeated, once for each value in a sequence.
- The value is held in a variable - which is defined in the first editable field when you create a new for instruction.
- The second field in the instruction specifies the sequence of values that will be assigned to the variable, successively.
- The sequence of values may be specified:
- as a literal list (of any kind), or a variable of type list
- as a string value, in which case each single character that makes up the string is assigned to the variable in succession
- as any expression that generates a List
- When a sequence of consecutive integer values is needed the most common way to do this is by calling one of these two functions
- range requires two arguments: the lower and upper bound; the upper bound is 'exclusive' so that
range(1, 11)range(1, 11)range(1, 11)range(1, 11)range(1, 11)
creates the sequence 1,2,3,4,5,6,7,8,9,10
- rangeInSteps requires three arguments: lower bound, upper bound (exclusive), and the 'step' or increment value, which can
be negative provided that the upper bound is then less than the lower bound.
- The variable is updated each pass around the loop; you should not attempt to modify the variable defined in the loop's signature from within the loop.
Call procedure
Examples of procedure calls
From the Burrow demo: a call to the library procedured named displayBlocks,
passing in the data structure held in variable blocks:
Code does not parse as Elan.blocks:
Code does not parse as Elan.blocks:
Code does not parse as Elan.blocks:
Code does not parse as Elan.blocks:
Code does not parse as Elan.
call displayBlocksprocedureName?(blocksarguments?)0
displayBlocksprocedureName?(blocksarguments?) # call procedure0
displayBlocksprocedureName?(blocksarguments?); // call procedure0
displayBlocksprocedureName?(blocksarguments?) ' call procedure0
displayBlocksprocedureName?(blocksarguments?); // call procedure0
From the Bubbles demo, a call from within the main routine to the procedure named moveGrowBurst procedure:
call moveGrowBurstprocedureName?(bubblesarguments?)0
moveGrowBurstprocedureName?(bubblesarguments?) # call procedure0
moveGrowBurstprocedureName?(bubblesarguments?); // call procedure0
moveGrowBurstprocedureName?(bubblesarguments?) ' call procedure0
moveGrowBurstprocedureName?(bubblesarguments?); // call procedure0
which is defined immmediately below the main routine at the same (global) level.
From the Snake - procedural demo, a procedure that contains a sequence of four calls to other procedures:
+procedure updateSnakename?(currentDirRef as AsRef<of Direction>, tailRef as AsRef<of List<of Int>>, headRef as AsRef<of List<of Int>>, body as List<of List<of Int>>parameter definitions?)
1
variable headname? set to headRef.value()value or expression?2
variable tailname? set to tailRef.value()value or expression?3
variable currentDirname? set to currentDirRef.value()value or expression?4
reassign currentDirvariableName? to directionByKey(currentDir, getKey())value or expression?5
call tailRef.setprocedureName?(body[0]arguments?)6
call body.appendprocedureName?(headarguments?)7
call headRef.setprocedureName?(getAdjacentSquare(head, currentDir)arguments?)8
call currentDirRef.setprocedureName?(currentDirarguments?)9
end procedure
+def updateSnakename?(currentDirRef: AsRef[Direction], tailRef: AsRef[list[int]], headRef: AsRef[list[int]], body: list[list[int]]parameter definitions?) -> None:
# procedure1
headname? = headRef.value()value or expression? # variable definition2
tailname? = tailRef.value()value or expression? # variable definition3
currentDirname? = currentDirRef.value()value or expression? # variable definition4
currentDirvariableName? = directionByKey(currentDir, getKey())value or expression? # reassign variable5
tailRef.setprocedureName?(body[0]arguments?) # call procedure6
body.appendprocedureName?(headarguments?) # call procedure7
headRef.setprocedureName?(getAdjacentSquare(head, currentDir)arguments?) # call procedure8
currentDirRef.setprocedureName?(currentDirarguments?) # call procedure9
+static void updateSnakename?(AsRef<Direction> currentDirRef, AsRef<List<int>> tailRef, AsRef<List<int>> headRef, List<List<int>> bodyparameter definitions?) {
// procedure1
var headname? = headRef.value()value or expression?;2
var tailname? = tailRef.value()value or expression?;3
var currentDirname? = currentDirRef.value()value or expression?;4
currentDirvariableName? = directionByKey(currentDir, getKey())value or expression?; // reassign variable5
tailRef.setprocedureName?(body[0]arguments?); // call procedure6
body.appendprocedureName?(headarguments?); // call procedure7
headRef.setprocedureName?(getAdjacentSquare(head, currentDir)arguments?); // call procedure8
currentDirRef.setprocedureName?(currentDirarguments?); // call procedure9
} // procedure
+Sub updateSnakename?(currentDirRef As AsRef(Of Direction), tailRef As AsRef(Of List(Of Integer)), headRef As AsRef(Of List(Of Integer)), body As List(Of List(Of Integer))parameter definitions?)
' procedure1
Dim headname? = headRef.value()value or expression? ' variable definition2
Dim tailname? = tailRef.value()value or expression? ' variable definition3
Dim currentDirname? = currentDirRef.value()value or expression? ' variable definition4
currentDirvariableName? = directionByKey(currentDir, getKey())value or expression? ' reassign variable5
tailRef.setprocedureName?(body[0]arguments?) ' call procedure6
body.appendprocedureName?(headarguments?) ' call procedure7
headRef.setprocedureName?(getAdjacentSquare(head, currentDir)arguments?) ' call procedure8
currentDirRef.setprocedureName?(currentDirarguments?) ' call procedure9
End Sub
public class Global {
+static void updateSnakename?(AsRef<Direction> currentDirRef, AsRef<List<int>> tailRef, AsRef<List<int>> headRef, List<List<int>> bodyparameter definitions?) {
// procedure1
var headname? = headRef.value()value or expression?;2
var tailname? = tailRef.value()value or expression?;3
var currentDirname? = currentDirRef.value()value or expression?;4
currentDirvariableName? = directionByKey(currentDir, getKey())value or expression?; // reassign variable5
tailRef.setprocedureName?(body[0]arguments?); // call procedure6
body.appendprocedureName?(headarguments?); // call procedure7
headRef.setprocedureName?(getAdjacentSquare(head, currentDir)arguments?); // call procedure8
currentDirRef.setprocedureName?(currentDirarguments?); // call procedure9
} // procedure
}
Note that in each of those
calls, the name of the procedure being called is preceded by an variable name and dot. This is known as 'dot syntax' and is used when the
procedure is defined as a procedure method on a specified type. For example, in the call to body.append(head)body.append(head)body.append(head)body.append(head)body.append(head), append
is a procedure method on the type ListlistListListList, which is the type of the bodybodybodybodybody variable.
What you need to know about procedure calls
- The call procedure instruction is used when you want to run a procedure .
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.
Procedures may have side effects, for example input/output or changing a data value in an object.
For this reason, procedures cannot be called from functions, which are not allowed to have side effects.
To enforce this, call procedure instructions are not allowed in functions.
There is a limit to the complexity of a call procedure instruction.
Only one dot is allowed in the procedure name field, or two dots if the first word is property.
If you need anything more complicated, use a change variable instruction on the line above.
See the error message explanation for 'procedureName' in a call instruction .
More advanced techniques
TODO: write afresh descriptions of mutating a (mutable) parameter, and, separately passing by Ref.
Try
Examples try statements
From the Date Time demo, where the input may be "Q""Q""Q""Q""Q" (to quit), an empty string
(to get the current date/time), or may be a numeric value to be converted. The function call int(reply)int(reply)int(reply)int(reply)int(reply)
attempts to read a string as an integer, but if it is not a valid integer will throw an exception. A try instruction
is used to intercept (or 'catch') this exception and just ignore that input:
+main
1
variable replyname? set to ""value or expression?2
+while not reply.upperCase().equals("Q")condition?
3
reassign replyvariableName? to input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression?4
+if reply.equals("")condition? then
5
variable nowname? set to divAsInt(clock(), 1000)value or expression?6
print(nowarguments?)7
print(getDate(now)arguments?)8
else9
+try
10
variable tdname? set to int(reply)value or expression?11
+if td >= 0condition? then
12
print(getDate(td)arguments?)13
end if
catch evariableName? as ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError?14
end try
end if
end while
end main
+def main() -> None:
1
replyname? = ""value or expression? # variable definition2
+while not reply.upperCase().equals("Q")condition?:
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression? # reassign variable4
+if reply.equals("")condition?:
5
nowname? = divAsInt(clock(), 1000)value or expression? # variable definition6
print(nowarguments?)7
print(getDate(now)arguments?)8
else:9
+try:
10
tdname? = int(reply)value or expression? # variable definition11
+if td >= 0condition?:
12
print(getDate(td)arguments?)13
except ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? as evariableName?: # catch14
main()
+static void main() {
1
var replyname? = ""value or expression?;2
+while (!reply.upperCase().equals("Q")condition?) {
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression?; // reassign variable4
+if (reply.equals("")condition?) {
5
var nowname? = divAsInt(clock(), 1000)value or expression?;6
Console.WriteLine(nowarguments?); // print7
Console.WriteLine(getDate(now)arguments?); // print8
} else {9
+try {
10
var tdname? = int(reply)value or expression?;11
+if (td >= 0condition?) {
12
Console.WriteLine(getDate(td)arguments?); // print13
} // if
} catch (ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? evariableName?) {14
} // try
} // if
} // while
} // main
+Sub main()
1
Dim replyname? = ""value or expression? ' variable definition2
+While Not reply.upperCase().equals("Q")condition?
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression? ' reassign variable4
+If reply.equals("")condition? Then
5
Dim nowname? = divAsInt(clock(), 1000)value or expression? ' variable definition6
Console.WriteLine(nowarguments?) ' print7
Console.WriteLine(getDate(now)arguments?) ' print8
Else9
+Try
10
Dim tdname? = int(reply)value or expression? ' variable definition11
+If td >= 0condition? Then
12
Console.WriteLine(getDate(td)arguments?) ' print13
End If
Catch evariableName? As ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError?14
End Try
End If
End While
End Sub
public class Global {
+static void main() {
1
var replyname? = ""value or expression?;2
+while (!reply.upperCase().equals("Q")condition?) {
3
replyvariableName? = input("RETURN for time now or Unix time (positive integer) or Q to quit")value or expression?; // reassign variable4
+if (reply.equals("")condition?) {
5
var nowname? = divAsInt(clock(), 1000)value or expression?;6
System.out.println(nowarguments?); // print7
System.out.println(getDate(now)arguments?); // print8
} else {9
+try {
10
var tdname? = int(reply)value or expression?;11
+if (td >= 0condition?) {
12
System.out.println(getDate(td)arguments?); // print13
} // if
} catch (ElanRuntimeErrortype e.g. ElanRuntimeError or CustomError? evariableName?) {14
} // try
} // if
} // while
} // main
}
What you need to know about try instructions
- The try instruction is a mechanism to execute code that might result in an 'exception' (called an 'error' in some languages),
and to catch that exception without the program being automatically halted.
- The try instruction is often referred to as a `try-catch` - because it makes no sense to have
a try statement without a 'catch' clause.
- When you add a try instruction in Elan, the 'catch' clause is automatically included in the instruction frame (template).
- 'except' is Python's word for 'catch'
- When you add a new try the instructions that you want to try to execute are specified immediately underneath the first line.
- Underneath the catch clause you may instructions to handle the exception, for example by advising the user to try again.
- The catch clause must specify the type of the exception to be caught. When working in Elan, only two types are permitted:
ElanRuntimeErrorElanRuntimeErrorElanRuntimeErrorElanRuntimeErrorElanRuntimeError which covers all errors thrown by the Elan system, including, for example 'Out of range index ...'
CustomErrorCustomErrorCustomErrorCustomErrorCustomError an exception explicitly thrown by code by a throw instruction in your own code (see below).
- Currently Elan permits only one catch clause to be associated with each try
You can enclose code in a try construction 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 .
The try construction automatically provides a catch clause in which you provide a String for an message.
You can also use a test to check whether another piece of code might throw an exception by wrapping it in a try instruction.
Throw
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 of a throwing an exception
The following function to calculate factorials deliberately 'throws' an exception (error) if it is given
a negative value for the argument:
+function factorialname?(n as Intparameter definitions?) returns IntType?
1
variable resultname? set to 1value or expression?2
+if n > 1condition? then
3
reassign resultvariableName? to n*factorial(n - 1)value or expression?4
elif n < 0condition? then5
throw CustomErrorexception type? "Factorial cannot be called with a negative argument"message?6
end if
return resultvalue or expression?7
end function
+def factorialname?(n: intparameter definitions?) -> intType?:
# function1
resultname? = 1value or expression? # variable definition2
+if n > 1condition?:
3
resultvariableName? = n*factorial(n - 1)value or expression? # reassign variable4
elif n < 0condition?: # else if5
raise CustomErrorexception type?("Factorial cannot be called with a negative argument"message?)6
return resultvalue or expression?7
+static intType? factorialname?(int nparameter definitions?) {
// function1
var resultname? = 1value or expression?;2
+if (n > 1condition?) {
3
resultvariableName? = n*factorial(n - 1)value or expression?; // reassign variable4
} else if (n < 0condition?) {5
throw new CustomErrorexception type?("Factorial cannot be called with a negative argument"message?);6
} // if
return resultvalue or expression?;7
} // function
+Function factorialname?(n As Integerparameter definitions?) As IntegerType?
1
Dim resultname? = 1value or expression? ' variable definition2
+If n > 1condition? Then
3
resultvariableName? = n*factorial(n - 1)value or expression? ' reassign variable4
ElseIf n < 0condition? Then5
Throw New CustomErrorexception type?("Factorial cannot be called with a negative argument"message?)6
End If
Return resultvalue or expression?7
End Function
public class Global {
+static intType? factorialname?(int nparameter definitions?) {
// function1
var resultname? = 1value or expression?;2
+if (n > 1condition?) {
3
resultvariableName? = n*factorial(n - 1)value or expression?; // reassign variable4
} else if (n < 0condition?) {5
throw new CustomErrorexception type?("Factorial cannot be called with a negative argument"message?);6
} // if
return resultvalue or expression?;7
} // function
}
What you need to know about throwing an exception
- The throw instruction will cause an 'exception' to be 'thrown' (or 'raised' in some languages).
- Unless the throw instruction is directly or indirectly within a Try instruction (see above),
program execution will be halted and the message defined in the throw instruction printed on the display.
- Because of this drastic effect it is recommended not to throw exceptions only in rare circumstances.
Assert
Examples of asserts
From the Date Time demo, showing asserts for normal and edge cases
new code
new code
new code
new code
public class Global {
new code
}
test test_leap
# normal cases
assert leap(2025) is false
assert leap(2024) is true
# boundary cases
assert leap(1900) is false
assert leap(2000) is true
end test
From the Tower of Hanoi
demo, showing an assert for an error case:
new code
new code
new code
new code
public class Global {
new code
}
From the Tower of Hanoi
demo, showing an assert for an error case:
+test test_colourtest_name?
1
#
assert colour(5)actual (computed) value? is greenexpected value? not run2
#
assert colour(1)actual (computed) value? is redexpected value? not run3
assert colour(10)actual (computed) value? is 0xFF99CCexpected value? not run4
#
assert colour(11)actual (computed) value? is "Out of range index: 10 size: 10"expected value? not run5
end test
+def test_colourtest_name?(self) -> None:
1
#
self.assertEqual(colour(5)actual (computed) value?, greenexpected value?) not run2
#
self.assertEqual(colour(1)actual (computed) value?, redexpected value?) not run3
self.assertEqual(colour(10)actual (computed) value?, 0xFF99CCexpected value?) not run4
#
self.assertEqual(colour(11)actual (computed) value?, "Out of range index: 10 size: 10"expected value?) not run5
+[TestMethod] static void test_colourtest_name?() {
1
//
Assert.AreEqual(greenexpected value?, colour(5)actual (computed) value?); not run2
//
Assert.AreEqual(redexpected value?, colour(1)actual (computed) value?); not run3
Assert.AreEqual(0xFF99CCexpected value?, colour(10)actual (computed) value?); not run4
//
Assert.AreEqual("Out of range index: 10 size: 10"expected value?, colour(11)actual (computed) value?); not run5
} // test
+<TestMethod> Sub test_colourtest_name?()
1
'
Assert.AreEqual(greenexpected value?, colour(5)actual (computed) value?) not run2
'
Assert.AreEqual(redexpected value?, colour(1)actual (computed) value?) not run3
Assert.AreEqual(&HFF99CCexpected value?, colour(10)actual (computed) value?) not run4
'
Assert.AreEqual("Out of range index: 10 size: 10"expected value?, colour(11)actual (computed) value?) not run5
End Sub
public class Global {
+@Test static void test_colourtest_name?() {
1
//
assertEquals(greenexpected value?, colour(5)actual (computed) value?); not run2
//
assertEquals(redexpected value?, colour(1)actual (computed) value?); not run3
assertEquals(0xFF99CCexpected value?, colour(10)actual (computed) value?); not run4
//
assertEquals("Out of range index: 10 size: 10"expected value?, colour(11)actual (computed) value?); not run5
} // test
}
What you need to know about asserts
- An assert instruction is used to test an individual case within a test, or sometimes to test individual
result values in a single case that returns a data structure.
- The assert defines two fields that the user must specify:
- The actual value as returned by a call to the function being tested.
- The expected value, which will typically be a literal value of the same type as the actual.
- When the tests are run (automatically whenever the code compiles) the result - pass or fail - will be
shown alongside each assert.
- A fail indicates that the expected and actual values do not match.
- The expected value should be of the same type as the actual; if they are not that will be reported as a test failure,
without showing the actual values.
- You can, however, test for an exception having been thrown, by setting the expected value to a the expected error message
(as a string).
- Unlike on many systems, Elan does not stop at the first failure within each test - it will attempt
to evaluate all the asserts.
- The required order of the actual and expected fields varies between the supported languages;
Elan handles this automatically when translating between languages.
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.1423.1423.1423.1423.142, in contrast to a value that is referred to by a name.
Here is a table showing some example literal values. Follow the links for more information about each Type.
| Type | Example of literal |
| Int | 33333 |
| Float | 2.02.02.02.02.0 |
| Boolean | true |
| String | "Hello""Hello""Hello""Hello""Hello" |
| Tuple | (3, "banana")(3, "banana")(3, "banana")(3, "banana")(3, "banana") |
| List | ["lemon", "lime", "orange"]["lemon", "lime", "orange"]["lemon", "lime", "orange"]{"lemon", "lime", "orange"}["lemon", "lime", "orange"] |
| Dictionary | ["a":3, "b":5]["a":3, "b":5]["a":3, "b":5]["a":3, "b":5]["a":3, "b":5] |
For more about List and Dictionary literals see the Standard data structures table.
Indexed values
If a named value is of an indexable type (StringstrstringStringString or ListlistListListList), then you can refer to a contiguous subset (or range) of its values
using methods subString and subList respectively.
The upper index of a range is exclusive, so to define a range that picks items indexed from i to j, you would specify the range as (i, j + 1)(i, j + 1)(i, j + 1)(i, j + 1)(i, j + 1).
If the two index values of a range are equal, or the second is smaller than the first,
then an empty string or list is returned.
Indexes are used only for reading values. Writing a value to a specific index location in a list is done using method
put .
Example 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
} // main
+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
public class Global {
+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
} // main
}
|
⟶ ⟶ ⟶ |
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
reassign 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? # reassign 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?; // reassign variable3
printprocedureName?(li[4]arguments?);4
printprocedureName?(li.subList(4, li.length())arguments?);5
printprocedureName?(li.subList(0, 7)arguments?);6
} // main
+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? ' reassign variable3
printprocedureName?(li[4]arguments?)4
printprocedureName?(li.subList(4, li.length())arguments?)5
printprocedureName?(li.subList(0, 7)arguments?)6
End Sub
public class Global {
+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?; // reassign variable3
printprocedureName?(li[4]arguments?);4
printprocedureName?(li.subList(4, li.length())arguments?);5
printprocedureName?(li.subList(0, 7)arguments?);6
} // main
}
|
⟶ ⟶ ⟶ |
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 FloatfloatdoubleDoubledouble or IntintintIntegerint arguments. The result may be a FloatfloatdoubleDoubledouble or an IntintintIntegerint depending on the arguments.
For the + − * operators, the result is a FloatfloatdoubleDoubledouble if either of the arguments is a FloatfloatdoubleDoubledouble, and an IntintintIntegerint if both arguments are IntintintIntegerint.
For the / operator, at least one of the values must be a FloatfloatdoubleDoubledouble, and the result is always a FloatfloatdoubleDoubledouble. It can be converted to an IntintintIntegerint 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?)0printprocedureName?(pow(4, 4)arguments?)0printprocedureName?(pow(4, 4)arguments?);0printprocedureName?(pow(4, 4)arguments?)0printprocedureName?(pow(4, 4)arguments?);0
variable cname? set to sqrt(pow(a, 2) + pow(b, 2))value or expression?0cname? = sqrt(pow(a, 2) + pow(b, 2))value or expression? # variable definition0var cname? = sqrt(pow(a, 2) + pow(b, 2))value or expression?;0Dim cname? = sqrt(pow(a, 2) + pow(b, 2))value or expression? ' variable definition0var 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 IntintintIntegerint arguments, and the result is IntintintIntegerint.
2/3.02/3.02/3.02/3.02/3.0 | ⟶ | 0.666.. |
2*32*32*32*32*3 | ⟶ | 6 |
2 + 32 + 32 + 32 + 32 + 3 | ⟶ | 5 |
2 - 32 - 32 - 32 - 32 - 3 | ⟶ | −1 |
11 mod 311 % 311 % 311 Mod 311 % 3 | ⟶ | 2 |
For integer division, either use method floor on a FloatfloatdoubleDoubledouble result, or use method divAsInt on FloatfloatdoubleDoubledouble arguments (which returns an IntintintIntegerint):
(11.0/3.0).floor()(11.0/3.0).floor()(11.0/3.0).floor()(11.0/3.0).floor()(11.0/3.0).floor() | ⟶ | 3 |
(-11.0/3.0).floor()(-11.0/3.0).floor()(-11.0/3.0).floor()(-11.0/3.0).floor()(-11.0/3.0).floor() | ⟶ | −4 |
divAsInt(11.0, 3.0)divAsInt(11.0, 3.0)divAsInt(11.0, 3.0)divAsInt(11.0, 3.0)divAsInt(11.0, 3.0) | ⟶ | 3 |
Note that rounding is always down.
Arithmetic operators follow the conventional rules for precedence i.e. 'BIDMAS' (or 'BODMAS').
When combining mod with other operators in an expression, insert round brackets to avoid ambiguity e.g.:
(5 + 6) mod 3(5 + 6) % 3(5 + 6) % 3(5 + 6) Mod 3(5 + 6) % 3 | ⟶ | 2 |
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:
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 BooleanboolboolBooleanbool arguments and return a BooleanboolboolBooleanbool result.
andandandandand and ororororor are binary operators
notnotnotnotnot is a unary operator.
The operator precedence is notnotnotnotnot → andandandandand → ororororor, so this
example, which implements an 'exclusive or', need not use brackets and can rely on the operator precedence:
+function exOrname?(a as Boolean, b as Booleanparameter definitions?) returns BooleanType?
1
return a and not b or b and not avalue or expression?2
end function
+def exOrname?(a: bool, b: boolparameter definitions?) -> boolType?:
# function1
return a and not b or b and not avalue or expression?2
+static boolType? exOrname?(bool a, bool bparameter definitions?) {
// function1
return a && !b || b && !avalue or expression?;2
} // function
+Function exOrname?(a As Boolean, b As Booleanparameter definitions?) As BooleanType?
1
Return a And Not b Or b And Not avalue or expression?2
End Function
public class Global {
+static boolType? exOrname?(bool a, bool bparameter definitions?) {
// function1
return a && !b || b && !avalue or expression?;2
} // function
}
String operator
The + operator is also used for concatenating StringstrstringStringString values.
Equality testing
Testing equality of IntintintIntegerint, FloatfloatdoubleDoubledouble or BooleanboolboolBooleanbool values
Equality testing of these Value Types uses the is and isnt operators between two values and returns a BooleanboolboolBooleanbool result:
- isnt returns the opposite of is.
(a is b)(a == b)(a == b)(a = b)(a == 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 FloatfloatdoubleDoubledouble and the other is of type IntintintIntegerint,
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 StringstrstringStringString values and of all Reference Types
Equality testing of these Reference Types uses common dot methods equals and notEqualTo:
-
+if listA.equals(listB)condition? then
0
new code
end if
+if listA.equals(listB)condition?:
0
new code
+if (listA.equals(listB)condition?) {
0
new code
} // if
+If listA.equals(listB)condition? Then
0
new code
End If
+if (listA.equals(listB)condition?) {
0
new code
} // if
reassign differsvariableName? to set1.notEqualTo(set2)value or expression?0differsvariableName? = set1.notEqualTo(set2)value or expression? # reassign variable0differsvariableName? = set1.notEqualTo(set2)value or expression?; // reassign variable0differsvariableName? = set1.notEqualTo(set2)value or expression? ' reassign variable0differsvariableName? = set1.notEqualTo(set2)value or expression?; // reassign 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 FloatfloatdoubleDoubledouble, but any named value or expression that evaluates to an IntintintIntegerint may always be used where a FloatfloatdoubleDoubledouble 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:
reassign xvariableName? to (a > b) and (b < c)value or expression?0xvariableName? = (a > b) and (b < c)value or expression? # reassign variable0xvariableName? = (a > b) && (b < c)value or expression?; // reassign variable0xvariableName? = (a > b) And (b < c)value or expression? ' reassign variable0xvariableName? = (a > b) && (b < c)value or expression?; // reassign variable0
reassign xvariableName? to (a + b) > (c - d)value or expression?0xvariableName? = (a + b) > (c - d)value or expression? # reassign variable0xvariableName? = (a + b) > (c - d)value or expression?; // reassign variable0xvariableName? = (a + b) > (c - d)value or expression? ' reassign variable0xvariableName? = (a + b) > (c - d)value or expression?; // reassign 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
} // main
+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
public class Global {
+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
} // main
}
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,
upperCaseupperCaseupperCaseupperCaseupperCase is a dot method that may be applied to any instance (variable or literal) of type StringstrstringStringString.
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 can use a lambda only as an argument to a Higher-order Function .
You would typically define a lambda when the functionality it defines is needed in only one location,
e.g. in a particular call to a Higher-order Function.
The syntax for a lambda is as follows:
- Start with the word 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.
For examples of using lambda in Higher-order Functions, see Library functions that process Lists
if (expression)
The if expression returns one of two values or expressions dependent upon the evaulation of a Boolean value:
Its syntax is shown in this reassign variable instruction:
variable isBiggername? set to if(a >= b, true, false)value or expression?0isBiggername? = if(a >= b, True, False)value or expression? # variable definition0var isBiggername? = if(a >= b, true, false)value or expression?;0Dim isBiggername? = if(a >= b, True, False)value or expression? ' variable definition0var isBiggername? = if(a >= b, true, false)value or expression?;0
Similarly, but returning an evaluated expression:
variable dname? set to if(c < 580, c - 40, c + 60)value or expression?0dname? = if(c < 580, c - 40, c + 60)value or expression? # variable definition0var dname? = if(c < 580, c - 40, c + 60)value or expression?;0Dim dname? = if(c < 580, c - 40, c + 60)value or expression? ' variable definition0var dname? = if(c < 580, c - 40, c + 60)value or expression?;0
in which variable d takes the value of c - 40c - 40c - 40c - 40c - 40 if c < 580c < 580c < 580c < 580c < 580, or c + 60c + 60c + 60c + 60c + 60 otherwise.
An if expression may be used within an if expression but may be hard to read and understand.
Examples using if expression:
● Choice in a return instruction:
+function fooname?(parameter definitionsparameter definitions?) returns IntType?
1
return if(isGreen(attempt, target, n), setChar(attempt, n, "*"), attempt)value or expression?2
end function
+def fooname?(parameter definitionsparameter definitions?) -> intType?:
# function1
return if(isGreen(attempt, target, n), setChar(attempt, n, "*"), attempt)value or expression?2
+static intType? fooname?(parameter definitionsparameter definitions?) {
// function1
return if(isGreen(attempt, target, n), setChar(attempt, n, "*"), attempt)value or expression?;2
} // function
+Function fooname?(parameter definitionsparameter definitions?) As IntegerType?
1
Return if(isGreen(attempt, target, n), setChar(attempt, n, "*"), attempt)value or expression?2
End Function
public class Global {
+static intType? fooname?(parameter definitionsparameter definitions?) {
// function1
return if(isGreen(attempt, target, n), setChar(attempt, n, "*"), attempt)value or expression?;2
} // function
}
● Using an if expression in a call procedure print:
+main
1
variable pname? set to unicode(0x03c0)value or expression?2
call testPiprocedureName?(p, 3arguments?)3
call testPiprocedureName?(p, 4arguments?)4
end main
+procedure testPiname?(p as String, v as Floatparameter definitions?)
5
call printprocedureName?(if(pi > v, $"{p} > {v}", $"{p} < {v}")arguments?)6
end procedure
+def main() -> None:
1
pname? = unicode(0x03c0)value or expression? # variable definition2
testPiprocedureName?(p, 3arguments?) # call procedure3
testPiprocedureName?(p, 4arguments?) # call procedure4
+def testPiname?(p: str, v: floatparameter definitions?) -> None:
# procedure5
printprocedureName?(if(pi > v, f"{p} > {v}", f"{p} < {v}")arguments?)6
main()
+static void main() {
1
var pname? = unicode(0x03c0)value or expression?;2
testPiprocedureName?(p, 3arguments?); // call procedure3
testPiprocedureName?(p, 4arguments?); // call procedure4
} // main
+static void testPiname?(string p, double vparameter definitions?) {
// procedure5
printprocedureName?(if(pi > v, $"{p} > {v}", $"{p} < {v}")arguments?);6
} // procedure
+Sub main()
1
Dim pname? = unicode(&H03c0)value or expression? ' variable definition2
testPiprocedureName?(p, 3arguments?) ' call procedure3
testPiprocedureName?(p, 4arguments?) ' call procedure4
End Sub
+Sub testPiname?(p As String, v As Doubleparameter definitions?)
' procedure5
printprocedureName?(if(pi > v, $"{p} > {v}", $"{p} < {v}")arguments?)6
End Sub
public class Global {
+static void main() {
1
var pname? = unicode(0x03c0)value or expression?;2
testPiprocedureName?(p, 3arguments?); // call procedure3
testPiprocedureName?(p, 4arguments?); // call procedure4
} // main
+static void testPiname?(String p, double vparameter definitions?) {
// procedure5
printprocedureName?(arguments?);6
} // procedure
}
|
⟶ ⟶ |
π > 3 π < 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?0headRefname? = AsRef[list[int]](head)value or expression? # variable definition0var headRefname? = new AsRef<List<int>>(head)value or expression?;0Dim headRefname? = New AsRef(Of List(Of Integer))(head)value or expression? ' variable definition0var headRefname? = new AsRef<List<int>>(head)value or expression?;0
Miscellaneous
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 definition
instruction and then use the named value in the 'actual' field of the assert equal
instruction. Some more complex expressions are permissible, but these two restrictions apply:
- 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 equal instruction.
'variable name' field in a set instruction
The first field in a change variable instruction most commonly takes the name of an existing variable.
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 procedure procedureName()
- call procedure instanceName.procedureMethodName()
- call procedure property.propertyName.procedureMethodName()
- call procedure 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:
ListlistListListList<
of IntintintIntegerint>
DictionaryDictionaryDictionaryDictionaryDictionary<
of StringstrstringStringString,
IntintintIntegerint>
'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 deconstruction of a
ListlistListListList or
Tuple .