Elan Library Reference
Value Types, with their methods
Integer
An integer or whole number, i.e. one that has no fractional component.
Type name
Int
Defining a literal integer
variable meaningOfLife set to 42
Default value
0
Dot methods
Method | Returns | Parameters | Description |
Functions |
asBinary | String | | |
asString | String | | |
Constraints
- Maximum value: 253 – 1 which is just over 9 × 1015
- Minimum value: – (253 – 1)
Notes:
- If either limit is exceeded the number will automatically be represented as a Float, with possible loss of precision.
- A value assigned to an Int may be expressed in decimal or, if preceded by 0x, in hexadecimal. Hexadecimal is useful for defining colours and Unicode codepoint values.
- An Int may always be passed as an argument into a method that specifies a Float.
Float
A ‘floating-point number’, i.e. a number that may have both integer and fractional parts.
Type name
Float
Defining literal floating-point value
variable a set to 1.618
variable b set to 1.1e-10
Dot methods
Method | Returns | Parameters | Description |
Functions |
asString | String | | |
ceiling | Int | | returns first integer larger than or equal to the floating-point number |
floor | Int | | returns first integer smaller than or equal to the floating-point number |
round | Float | placesInt | returns the number rounded to the number of decimal places specified as the argument |
Constraints
Since Elan compiles to JavaScript, the constraints on floating point numbers are those of JavaScript:
- Maximum value: just over 1 × 10308
- Minimum value: approximately 5 × 10–324
For greater detail, refer to the official JavaScript documentation.
A variable that has been defined as being of Type Float may not be passed as an argument into a method that requires an Int, nor as an index into an List, even if the variable contains no fractional part.
It may, however, be converted into an Int before passing, using the functions floor() or ceiling():
floor() returns the integer value left by removing any fractional part, and
ceiling() returns the lowest integer greater than the Float value if does have a fractional part.
If you wish to define a variable to be of Type Float but initialise it with an integer value then add .0 on the end of the whole number, for example:
variable a set to 3.0.
Boolean
A Boolean value is either true or false.
Type name
Boolean
Defining a literal Boolean
variable a set to true
true and false must be written lower case
Default value
false
Dot methods
Method | Returns | Parameters | Description |
Functions |
asString |
String |
as |
|
String
A String represents ‘text’ i.e. a sequence of zero or more characters.
Type name
String
Defining a literal string value
variable a set to "Hello"
Strings are always delineated by double-quotes.
Default value
"", known as ‘the empty string’.
Note
- As in most programming languages, strings are immutable. When you apply any operation or function with the intent of modifying an existing string, the existing string is never modified. Instead, the operation or function will return a new string that is based on the original, but with the specified differences.
Strings may be appended to using the plus operator, for example
print "Hello" + " " + "World"
A newline may be inserted within a string as \n, for example:
print "Hello\nWorld"
- You may insert single-quotes 'within a string.
Dot methods
Method | Returns | Parameters | Description |
Functions |
asString |
String |
|
returns the string itself. (All types must have this method to be print-able.) |
asList |
List<of String> |
|
returns list of individual characters |
asUnicode |
Int |
|
returns the Unicode value of the first character of the string |
asRegExp |
RegExp |
|
returns the same string converted to RegExp (does not check whether the result would be a valid regular expression) |
asSet |
Set |
|
returns a set of the unique characters |
|
|
as |
|
Procedures |
|
|
as |
|
- contains returns a Boolean indicating whether or not the string contains the sub-string specified as the argument.
- filter
- indexOf
- isAfter compares, alphabetically, the string to the String argument returning a Boolean
- isAfterOrSameAs compares, alphabetically, the string to the String argument, returning a Boolean
- isBefore compares, alphabetically, the string to the String argument, returning a Boolean
- isBeforeOrSameAs compares, alphabetically, the string to the String argument, returning a Boolean
- length returns the number of characters in the string as an Int
- lowerCasereturns a new string with the original rendered in lower-case.
- map
- matchesRegExpreturns a Int indicating whether or not the string matches the regular expression provided as an argument of type RegExp.
- reduce
- replace returns a new string with all occurrences of the string (specified as the first argument replaced by the string specified as the second argument.
- sortBy
- split splits the string at each occurrence of the specified 'separator' string, returning a List<of String> of the content between the separators.
- trim returns a new String with any leading and/or trailing spaces removed.
- upperCase returns a new string with the original rendered in upper-case.
Interpolated string
Elan strings are automatically interpolated. This means that you may insert the values of variables or simple expressions within a string by enclosing them in curly braces. For example (assuming that the variables a and b are already defined as integers) :
print "{a} times {b} equals {a*b}"
You cannot include the characters ", {, or } directly within a literal string because of their special meanings. Instead, you use the constants quotes, leftBrace and rightBrace respectively:
print "This is double quote mark: " + quotes
Alternatively, you can insert their Unicode codepoints by means of the unicode() standalone function:
print "This is a double quote mark: " + unicode(34)
or
print"Here are the curly braces: {unicode(123)} and {unicode(125)}"
Dot methods on a String
There are no ‘substring’ methods in Elan because you can always use an index range get a substring, e.g. s[3..7] gives a string containing the fourth character to the seventh character inclusive of string s. Note that the upper bound of the range is exclusive. See Indexed Value.
upperCase() returns String
returns a new string based on the input with all alpha-characters in upper case.
lowerCase() returns String
returns a new string based on the input with all alpha-characters in lower case.
contains(partString as String) returns Boolean
takes a single parameter of Type String, and returns a Boolean value indicating whether or not that argument string is contained within the string on which contains was called. Usage:
variable a set to "Hello World!"
print a.contains("ello")
prints true
replace(match as String, replacement as String) returns String
returns a new string where all occurrences of the match string are replaced with the replacement string.
trim() returns String
returns a new string based on the string on which the method is called, but with all leading and trailing spaces removed.
indexOf(partString as String) returns Int
The following methods are used for comparing strings alphabetically, for example in a sort routine:
isBefore(otherString as String) returns Boolean
isAfter(otherString as String) returns Boolean
isBeforeOrSameAs(otherString as String) returns Boolean
isAfterOrSameAs(otherString as String) returns Boolean
asUnicode() returns Int
returns the Unicode (integer) value for a character. If the string is more than one character long, the Unicode value returned is that for the first character in the string only. Note that the opposite method to create a single-character string from its numeric Unicode value is e.g. unicode(123) which returns "{".
Tuples
A tuple is a way of holding a small number of values of different Types together as a single reference. It may be considered a ‘lightweight’ alternative to defining a specific class for some purposes. Tuples are referred to as 2-tuples, 3-tuples, etc. according to the number of values they hold. Common uses include:
- Holding a pair of x and y coordinates (each of Type Float) as a single unit.
- Allowing a function to pass back a result comprised of both a message in a String and a Boolean indicating whether the operation was successful.
Using a tuple
You may pass a tuple into a function, or return one from a function, for example:
variable d set to distanceBetween(point1, tuple(12.34, 20.0))
An existing tuple (for example point1 below) may be ‘deconstructed’ into new variables or named values (where the number of variables/names must match the number of elements in the tuple):
let x, y set to point1
variable x, y set to point1
or into existing variables of the correct Type:
variable a set to 3
variable b set to 4
set a, b to point1
The ‘discard’ symbol _ (underscore) may also be used when deconstructing a tuple when there is no need to capture specific elements:
variable x, _ set to point1
Notes:
As in most languages, Elan tuples are immutable. Once defined they are effectively ‘read only’. You cannot alter any of the elements in a tuple nor (unlike a List for example) can you create a new tuple from an existing one with specified differences.
You cannot deconstruct a tuple into a mixture of new and existing variables.
tuples may be nested: you can define a tuple within a tuple.
Standard data structures
The four 'standard' data structures defined in the Elan library are summarised in the this table:
All four are 'mutable' - meaning that their contents may be changes directly by calling the various procedure dot-methods that each
type defines.
However, since procedure methods may be called only from within the main routine,
or from within another procedure, it is also possible to make changes via
function dot-methods - which return a copy of the current datastructure,
with the specified changes - which is why all such methods have names starting with....
All four data structures contain values of a single type - that type either being specified explicitly - for example
of <procedure> Int - or implictly if the structure
is created from its literal definition form. (Dictionary is defined by two types: one for the 'keys' and one for the 'values').
Array
An array is a simple data structure. The type for the members of the array must be one of:
Int, Float, String, or Boolean.
The size must also be specified when it is
created, along with the value (of the specific member type) to which each member is initialised. It is not possible to append
further items to an array, although it is possible to convert an array to a list using .asList() and then to extend or reduce that list.
As in most languages, individual elements may be read 'by index'. However, to modify an element you must either
call the put procedure-method, or use
either withPut function-method.
Array2D
The Array2D type defines a 2-dimensional arrays of a fixed size. The two
dimensions, which are specified when the array is created, may be of the same size (known as a 'square' array)
or different ('rectangular'). If you want to create a 'jagged' array, you should use a List of lists.
The type for the members is also specified when the array is defined and must be
one of: Int, Float,
String, or Boolean.
You can read individual elements with a double index, for example:
for col from 0 to 7 step 1
for row from 0 to 7 step 1
print board[col, row]
end for
end for
List
An List
While the members must all be of one type, that type is not limited to the simple value types:
the member type may also be a user-defined type such as a class or record, or another list, or even an array.
The size may be dynamically extended. Indeed a list is either created empty to begin with, or
initialised from a literal definition.
Dictionary
A dictionary works like an array, but instead of requiring a numeric (integer) index, each entry has a 'key'. The type of
the key - which must be one of Int, Float, String, or Boolean
or a user-defined record type - is specified when the dictionary is created along with the type
for the values, which may be any type.
Immutable data structures
In constrast to the standard data structures, immutable
data structures cannot be modified directly - and hence define no procedure dot-methods. Instead,
changes are made by using function dot-methods that copy the existing data structure, but with specified
differences. Immutable data structures are intended specifically to facilitate the 'functional programming' paradigm,
but some are also useful within the procedural or functional programming paradigms.
The five immutable data structures defined in the Elan library are summarised in the this table:
More details on each type are given below:
Set
A Set is a standard data structure that works somewhat like a ListImmutable with the important difference that in a Set a given element may appear only once. If an item being added to a Set is identical to an existing item in the Set then the Set remains the same length as before.
This enables a Set to work like a mathematical set so that it is possible to perform standard set operations such as union or intersection. For the same reason, a Set is an immutable data structure: there are no methods modify the set on which they are called, but several of them (including add, remove) return a new Set that is based on the original Set or Sets, with specified differences.
Example of use:
+main
variable stname set to new Set<of Int>()expression
set stvariableName to st.addFromList({3, 5, 7})expression
print st.length()expression
set stvariableName to st.add(7)expression
print st.length()expression
set stvariableName to st.remove(3)expression
print st.length()expression
set stvariableName to st.remove(3)expression
print st.length()expression
print stexpression
end main
Notes
- When creating a Set, the Type of the elements must be specified in the form
Set<of String>. This applies both when creating a new, empty set and when defining the Type of a parameter to be a Set.
- You can add elements: individually with add, or multiple elements with addFromList and addFromList.
- You can create a new Set from an existing List or ListImmutable by calling .asSet() on it.
Available dot methods on a Set:
print s.length()expression
print s.contains(item)expression
print s.add(item)expression
print s.addFromList(list)expression
print s.addFromList(array)expression
print s.remove(item)expression
print s.union(anotherSet)expression
print s.difference(anotherSet)expression
print s.intersection(anotherSet)expression
print s.isDisjointFrom(anotherSet)expression
print s.isSubsetOf(anotherSet)expression
print s.isSupersetOf(anotherSet)expression
print s.asList(anotherSet)expression
print s.asImmutableList(anotherSet)expression
print s.asString()expression
Stack and queue
- Stack and Queue are similar data structures except that Stack is ‘LIFO’ (last in, first out), while Queue is FIFO (first in, first out). The names of the methods for adding/removing are different, but there are also common methods, summarised here.
- Both a Stack and a Queue are defined with the Type of the items that they can contain, similarly to how List and ListImmutable have a specified item Type, though with different syntax. The Type is specified in the form shown below e.g. Stack<of String>, Queue<of Int>, Stack<of (Float, Float)>, Queue<of Square>.
- Both Stack and Queue are dynamically extensible, like List and ListImmutable. There is no need (or means to) specify a size limit as they will continue to expand until, eventually, the computer’s memory limit is reached.
- This same syntax is used to specify the Type if you want to pass a Stack into a function, or specify it as the return Type.
- Stack and Queue have some methods in common: length(), and peek() which allows you to read the next item that would be removed, without actually removing it.
- The names of the methods for adding or removing an item are different for Stackk and Queue, as summarised in this table:
| Stack | Queue |
Create a new instance | let s be new Stack<of Int>() | let q be new Queue<of Int>() |
Add an item (must be of correct Type) | call s.push(item) | call q.enqueue(item) |
Remove the next item | variable item set to s.pop() | variable item set to q.dequeue() |
View the next item to be removed without removing it | variable item set to s.peek() | variable item set to q.peek() |
Read the current length | s.length() | q.length() |
Example use of a Stack:
+main
let skname be new Stack<of String>()expression
print sk.length()expression
call sk.pushprocedureName("apple"arguments)
call sk.pushprocedureName("pear"arguments)
print sk.length()expression
print sk.peek()expression
variable fruitname set to sk.pop()expression
print fruitexpression
set fruitvariableName to sk.pop()expression
print fruitexpression
print sk.length()expression
end main
Example use of a Queue:
+main
let quname be new Queue<of String>()expression
print qu.length()expression
call qu.enqueueprocedureName("apple"arguments)
call qu.enqueueprocedureName("pear"arguments)
print qu.length()expression
print qu.peek()expression
variable fruitname set to qu.dequeue()expression
print fruitexpression
set fruitvariableName to qu.dequeue()expression
print fruitexpression
print qu.length()expression
end main
ListImmutable
A ListImmutable is like a List but is immutable (like a String)
You can still insert, delete, or change elements in a ListImmutable, but the methods for these operations do not modify the input ListImmutable: they return a new ListImmutable based on the input ListImmutable but with the specified differences.
Type name
The Type is specified in the following way:
ListImmutable<of String> for a ListImmutable of TypeString
ListImmutable<of Int> for a ListImmutable of Type >Int
ListImmutable<of ListImmutable<of Int>> for a ListImmutable of Lists of Type Int
Creating a ListImmutable
A ListImmutable may be defined in ‘literal’ form, ‘delimited’ by curly braces, and with all the required elements separated by commas. The elements may be literal values but must all be of the same Type):
variable fruit set to {"apple", "orange", "pear"}
Dot methods on a ListImmutable
The dot methods on a list are all functions.
myList.contains(item) returns true or false
myList.asList() returns a new List with the same contents as myList
The following functions all return a new ListImmutable, copied from the ListImmutable on which the function was called, but with the differences specified by the function parameters:
myList.withInsert(4, "cherry")
myList.withPut(2, "grape")
myList.withRemoveAt(3)
myList.withRemoveFirst("apple")
myList.withRemoveAll("apple")
Try these examples:
variable fruit set to empty ListImmutable<of String>
print fruit
set fruit to fruit + "apple"
set fruit to fruit + "pear"
print fruit
set fruit to "orange" + "pear"
print fruit[0]
print fruit.length()
print fruit[fruit.length() -1]
variable head:tail set to fruit
print head
print tail
DictionaryImmutable
An immutable dictionary may be defined in a constant.
Type name
In the following example, the keys are of Type Int, and the values associated with the keys are of Type String:
DictionaryImmutable<of String, Int>
Defining a literal DictionaryImmutable
A literal DictionaryImmutable is defined as a comma-separated list of ‘key:value pairs’ (key,colon.value) surrounded by curly braces:
variable scrabbleValues set to {"a":1, "b":3, "c":3, "d":2}
Using an Immutable Dictionary
Try these examples:
variable immD set to new DictionaryImmutable<of String,Int>()
print immD
set immD to immD.withPutKey("a", 3)
print immD["a"]
set immD to immD.withRemoveAtKey("a")
print immD
Dot methods on an Immutable Dictionary
See also Quick reference.
hasKey
withPutKey
withRemoveAtKey
Input/output
Reading Text Files
The TextFileReader class is used to read textual data from a file.
An instance is created by the standalone system method openFileForReading, on
which the dot-methods the following methods may be invoked:
- readLine
- readWholeFile
- endOfFile
- close
These methods may be used to read a whole file in one go:
let filename be openFileForReading()expression
let textname be file.readWholeFile()expression
call file.closeprocedureName(arguments)
print textexpression
or to read a file line by line:
+main
let filename be openFileForReading()expression
variable linesname set to empty List<of String>expression
+while not file.endOfFile()condition
let linename be file.readLine()expression
call lines.appendprocedureName(linearguments)
end while
call file.closeprocedureName(arguments)
Notes
- openFileForReading will present the user with a dialog to select the file.
- readWholeFile returns a String containing every character in the file, without any trimming. It automatically closes the file after the read.
- readLine reads as far as the next newline character (\n) and then automatically trims the line to remove any spaces and/or carriage-returns (which some file systems insert after the newline automatically) from the resulting line returned as a String. If this behaviour is not desired, you can use readWholeFile, which does no trimming, and then parse the resulting String into separate lines.
- Calling file.close() after reading line by line is strongly recommended to avoid any risk of leaving the file locked. It is not necessary to call it after using readWholeFile() because that method automatically closes the file.
- Calling any method on a file that is already closed will result in a runtime error.
Writing text files
The TextFileWriter class is used to write textual data to a file.
An instance is created by the standalone system method createFileForWriting, on
which the dot-methods the following methods may be invoked:
- writeLine
- writeWholeFile
- saveAndClose
These methods may be used to write a whole file in one go:
let fname be createFileForWriting("myFile.txt")expression
call f.writeWholeFileprocedureName("this is\nmyText"arguments)
or to write a file line by line:
+main
let filename be createFileForWriting("squares.txt")expression
+for ivariableName from 1expression to 100expression step 1expression
call file.writeLineprocedureName("{i} {i*i}"arguments)
end for
call file.saveAndCloseprocedureName(arguments)
end main
Notes
- writeLine adds the string it is passed onto the end of any data previously written, with a newline character (\n) automatically appended.
- When execution reaches saveAndClose() you will be presented with a dialog to confirm (or edit) the given filename and location where it is to be saved. It is not therefore strictly necessary to specify a filename when creating the file, since it can be specified by the user in the dialog so, in that case, you might put the empty string "" into the parameter of createFileForWriting.
- writeWholeFile puts the string it is given into the file and then automatically saves the file, so the user will be presented with the same dialog as if saveAndClose had been called.
- Calling any method on a file that has already been closed (by calling either saveAndClose or by writeWholeFile) will result in a runtime error.
- If the user were to hit Cancel on the save dialog, then the program will exit with an error. If you want to guard against this possibility (if, for example, it might mean the loss of important data) then you should perform the save and close within a try..catch like this:
+try
call file.saveAndCloseprocedureName(arguments)
+catch exception in evariableName
print "File save cancelled"expression
end try
or you could make the code offer the user options: to save again, or to continue without saving.
Block graphics
Block graphics provides a simple way to create low resolution graphics, ideal for simple but engaging games for example.
The graphics are displayed on a grid that is 40 blocks wide by 30 blocks high.
Each block is be rendered as a solid colour.
An example of block graphics to produce a rapidly changing pattern of coloured blocks:
+main1
variable blocksname? set to new Array2D<of Int>(40, 30, white)expression?2
+while truecondition?3
let xname? be randomInt(0, 39)expression?4
let yname? be randomInt(0, 29)expression?5
let colourname? be randomInt(0, white - 1)expression?6
call blocks.putprocedureName?(x, y, colourarguments?)7
call displayBlocksprocedureName?(blocksarguments?)8
end while
end main
Notes
- The Array2D must be of type Int and of size 40 x 30.
- Colours are specified as an integer value, either using:
- the limited colours defined as library constants (see Colour)
#
- an integer in the decimal range 0 (black) to 224-1 (white)
- as a six digit hexadecimal value in the range 0x000000 0xffffff
using the same 'RGB' format as used in Html style - for example 0xff0000 for red.
- You may create multiple Array2Ds holding different patterns of blocks, and switch between them
just by passing the required one as the argyument to the displayBlocks method.
Turtle graphics
Example code:
+main
let tname be new Turtle()expression
call t.placeAtprocedureName(10, 10arguments)
call t.showprocedureName(arguments)
+for ivariableName from 1expression to 4expression step 1expression
call t.turnprocedureName(90arguments)
call t.moveprocedureName(40arguments)
call t.pauseprocedureName(500arguments)
end for
end main
Output:
Notes
- move and turn are the two most commonly-used methods. To move backwards, specify a negative value. The value passed into turn is interpreted as degrees: a positive value turns clockwise; a negative value anti-clockwise. Both methods take a numeric value, which may be an Int or a Float.
- Scaling: the argument provided to the move procedure is specified in ‘turtle-units’. The Graphics pane on the screen (i.e. the ‘paper’ on which the Turtle draws) is 100 turtle units wide by 75 turtle units high. If the turtle is moved outside these boundaries it will not cause an error, but the location of the turtle and any lines outside the boundaries will not be visible.
- show causes the turtle to be displayed (the small green circle with a black radius showing the direction it is pointing); hide does the opposite. You can move and turn the turtle, causing lines to be drawn, whether or not the turtle is shown.
- To move the turtle without drawing a line call penUp, then penDown when you are ready to draw lines again.
- penColour takes an integer argument specifying the colour. For specifying colours, see Colour.
- penWidth specifies the width of the line drawn by the turtle which must be an integer. The default is the minimum value of 1.
- You can specify the start position of the turtle in x,y coordinates (0,0 being the top-left of the Graphics pane) with placeAt, which may also be used to reposition the turtle (without drawing a connecting line) during the program run. You may specify the turtle’s absolute heading with turnTo, where 0 would cause the turtle to face up the screen.
- The current location and heading of the turtle may be read using the x, y, and heading properties.
- There is no difference in effect between call t.pause(500) and the standalone call pause(500): the former option is provided as a convenience, because most instructions in a Turtle program take the form call t.something. Both versions take an integer argument, being the length of the pause in milliseconds.
- Apart from the penColour and pause methods, both of which require an integer value, all other procedure methods on the Turtle can take integer or floating-point values.
Here is a more sophisticated example, using a procedure and recursion, that produces a fractal snowflake:
+main
variable tname set to new Turtle()expression
call t.placeAtprocedureName(20, 20arguments)
call t.turnprocedureName(90arguments)
call t.showprocedureName(arguments)
+for ivariableName from 1expression to 3expression step 1expression
call drawSideprocedureName(side, targuments)
call t.turnprocedureName(120arguments)
end for
end main
+procedure drawSidename(length as Float, t as Turtleparameter definitions)
+if (length > 1)condition then
let thirdname be length/3expression
call drawSideprocedureName(third, targuments)
call t.turnprocedureName(-60arguments)
call drawSideprocedureName(third, targuments)
call t.turnprocedureName(120arguments)
call drawSideprocedureName(third, targuments)
call t.turnprocedureName(-60arguments)
call drawSideprocedureName(third, targuments)
else if
call t.moveprocedureName(lengtharguments)
end if
end procedure
+constant sidename set to 60literal value or data structure

Vector graphics
Example:
+main1
variable vgname? set to new List<of VectorGraphic>()expression?2
let circname? be new CircleVG(20, 20, 5, red, green, 2)expression?3
call vg.appendprocedureName?(circarguments?)4
call displayVectorGraphicsprocedureName?(vglistOfVGs?)5
end main
Output:

Notes
- Elan vector graphics are displayed using SVG (Scalable Vector Graphics) that are a part of the Html specification. The names of the shapes broadly correspond to the names of SVG tags: CircleVG for <circle../>, LineVG for <line../>, and RectangleVG for <rect../>.
The properties of the Elan VG shapes match the names of the attributes used in the SVG tags, except that the stroke-width attribute is changed to strokeWidth to make it a valid Identifier.
- The ‘canvas’ on which vector graphics are drawn (the Graphics pane in the user interface) is 100 units wide, by 75 units high .All numeric values specified for attributes of vector graphic shapes may be integer or floating point.
- The constructor for each VG type requires arguments corresponding the Html attributes for the corresponding SVG type.
- As with Block graphics the screen is not updated until the displayVectorGraphics method is called,
allowing you to make multiple changes before updating the screen. Similarly, the method to add a shape returns a new instance of the VectorGraphics which must be assigned either to an existing variable, or to a new let.
- As with the way that SVG works within Html, the shapes are drawn in the order in which they are added into the list of VectorGraphic instances, with later shapes positioned over earlier shapes.
- The colour (for stroke and fill properties) may be specified as described at Colour.
The fill colour only may also be specified as transparent (which has the value -1).
- VectorGraphic is the abstract superclass of all .VG shapes. You would only use if if you wanted to define a method that could work on any shape (using common members defined on VectorGraphic) or that could work with a List holding different types of shape.
+main1
variable vgname? set to new List<of VectorGraphic>()expression?2
let circname? be new CircleVG(50, 37, 30, green, black, 1)expression?3
call vg.appendprocedureName?(circarguments?)4
+while truecondition?5
call displayVectorGraphicsprocedureName?(vglistOfVGs?)6
call pauseprocedureName?(700arguments?)7
call circ.setFillColourprocedureName?(redarguments?)8
call displayVectorGraphicsprocedureName?(vglistOfVGs?)9
call pauseprocedureName?(700arguments?)10
call circ.setFillColourprocedureName?(greenarguments?)11
end while
end main
Notes:
- The constructor parameters for CircleVG are: centreX, centreY, radius, fillColour, strokeColour, strokeWidth
.
- The constructor parameters for LineVG are: x1, y1, x2, y2, strokeColour, strokeWidth, all of type Int
.
- The constructor parameters for LineVG are: x, y, width, height, fillColour, strokeColour, strokeWidth
.
- All parameters for the three constructors above are of type Int.
- Individual properties of any of the VG types may be modified by calling the corresponding set... procedure method,
or, if working within a function, by using the corresponding with... function method.
Other types
Func
A function may be passed as an argument into another function (or a procedure), or returned as the result of calling another function. This pattern is known as ‘Higher order Function’ (HoF), and is a key idea in the functional programming paradigm. To define a function that takes in another function as a parameter, or returns a function, you need to specify the Type of the function, just as you would specify the Type of every parameter and the return Type for the function.
Type name
The Type of any function starts with the word Func followed by angle brackets defining the Type of each parameter, and the return Type for that function, following this syntax:
Func<of String, String, Int => Boolean>
This example defines the Type for a function that defines three parameters of Type String, String, and Int respectively, and returns a Boolean value. This Type would match that of a function definition that started:
+function charactersMatchAtname(a as String, b as String, position as Intparameter definitions) returns BooleanType
Constants
name | Type | value |
openBrace | String | { |
closeBrace | String | } |
quotes | String | " |
Colours
colour | | decimal | decimal
| hexadecimal |
name | | integer | R G B | 0xrrggbb |
black | ◼ | 0 | 0  0 0 | 0x000000 |
white | ◼ | 16777215 | 255 255 255 | 0xffffff |
red | ◼ | 16711680 | 255 0 0 | 0xff0000 |
green | ◼ | 32768 | 0 128 0 | 0x008000 |
blue | ◼ | 255 | 0 0 255 | 0x0000ff |
yellow | ◼ | 16776960 | 255 255 0 | 0xffff00 |
brown | ◼ | 10824234 | 165 42 42 | 0xa52a2a |
grey | ◼ | 8421504 | 128 128 128 | 0x808080 |
transparent | | -1 | none | |
Standalone functions
Standalone library functions always return a value and are therefore used in contexts that expect a value, such as in the right-hand side of a variable declaration (variable) or assignment (set), either on their own or within a more complex expression. All standalone library functions require at least one argument to be passed in brackets, corresponding to the parameters defined for that function.
unicode
converts a Unicode value (expressed as an integer value in decimal or hexadecimal notation) into a single character string. For example:
+function heartsname(parameter definitions) returns StringType
return unicode(0x2665)expression
end function
parseAsInt and parseAsFloat
parseAsInt attempts to parse the input String as an Int and returns a 2-tuple, the first value of which is Boolean, with true indicating whether or not the parse has succeeded, and the second value being the resulting Int.
parseAsFloat does the equivalent for floating point. Operation is illustrated with by these tests:
+test
assert parseAsInt("31")computed value is tuple(true, 31)expected value pass
assert parseAsInt("0")computed value is tuple(true, 0)expected value pass
assert parseAsInt("thirty one")computed value is tuple(false, 0)expected value pass
assert parseAsInt("3.1")computed value is tuple(false, 0)expected value pass
assert parseAsFloat("31")computed value is tuple(true, 31)expected value pass
assert parseAsFloat("0")computed value is tuple(true, 0)expected value pass
assert parseAsFloat("3.1")computed value is tuple(true, 3.1)expected value pass
end test
Notes
- Any string that parses as an Int will also parse as a Float.
- If the parse fails, the second value will become zero, so you should always check the first value to see if the second value is a correct parse or just the default.
- You can ‘deconstruct’ the tuple into two variables:
variable success, parsedValuename
- One use of these parsing methods is for validating user input, but note that an easier way to do this is to use the various input methods.
floor, ceiling, round, isNaN, and IsInfinite
All of these functions are called as 'dot methods' on a numeric value of type Float or Int).
NaN is short for 'Not A (Real) Number' Their use is illustrated with the following tests:
+test
let nname be 3.14159expression
assert n.floor()computed value is 3expected value pass
assert n.ceiling()computed value is 4expected value pass
assert n.round(3)computed value is 3.142expected value pass
assert sqrt(-1).isNaN()computed value is trueexpected value pass
let xname be 1/0expression
assert x.isInfinite()computed value is trueexpected value pass
end test
Maths functions and constants
function | argument Type | input unit | returns | output unit |
pi | (none) | | 𝜋 = 3.141592653589793.. | |
abs | Float | | absolute value of the input | |
acos | Float | | arccosine of the input | radians |
asin | Float | | arcsine of the input | radians |
atan | Float | | arctangent of the input | radians |
acosDeg | Float | | arccosine of the input | degrees |
asinDeg | Float | | arcsine of the input | degrees |
atanDeg | Float | | arctangent of the input | degrees |
cos | Float | radians | cosine of the input | |
cosDeg | Float | degrees | cosine of the input | |
exp | Float | | 𝑒𝑥 where 𝑥 is the argument and 𝑒 is Euler's number 2.718281828459045.. the base of natural logarithms | |
logE | Float | | natural logarithm of the input | |
log10 | Float | | base-10 logarithm of the input | |
log2 | Float | | base-2 logarithm of the input | |
sin | Float | radians | sine of the input | |
sinDeg | Float | degrees | sine of the input | |
sqrt | Float | | positive square root of the input | |
tan | Float | radians | tangent of the input | |
tanDeg | Float | degrees | tangent of the input | |
degToRad | Float | degrees | converts input from degrees to radians | radians |
radToDeg | Float | radians | converts input from radians to degrees | degrees |
Examples of some maths functions being tested:
+test
assert picomputed value is 3.141592653589793expected value pass
assert abs(-3.7)computed value is 3.7expected value pass
assert asin(0.5).round(3)computed value is 0.524expected value pass
assert acos(0.5).round(3)computed value is 1.047expected value pass
assert atan(1).round(2)computed value is 0.79expected value pass
assert sin(pi/6).round(2)computed value is 0.5expected value pass
assert cos(pi/4).round(3)computed value is 0.707expected value pass
assert tan(pi/4).round(2)computed value is 1expected value pass
assert exp(2).round(3)computed value is 7.389expected value pass
assert logE(7.389).round(2)computed value is 2expected value pass
assert log10(1000)computed value is 3expected value pass
assert log2(65536)computed value is 16expected value pass
assert log2(0x10000)computed value is 16expected value pass
assert sqrt(2).round(3)computed value is 1.414expected value pass
end test
Regular expressions
Elan’s regular expressions are modelled on those of JavaScript, including the syntax for literal regular expressions. See, for example this Guide to Regular Expressions.
More functions for using regular expressions will be added in a future release of Elan. For now…
The method matchesRegExp() is applied to a String using dot syntax and requires a RegExp parameter specified as a literal or as variable. It returns a Boolean. For example:
+test
let s1name be "hello"expression
let s2name be "World"expression
let rname be /^[a-z]*$/expression
assert s1.matchesRegExp(r)computed value is trueexpected value pass
assert s2.matchesRegExp(r)computed value is falseexpected value pass
end test
You can convert a valid string without /../ delimiters to a RegExp using function asRegExp():
+test
let s1name be "hello"expression
let s2name be "World"expression
let rname be "^[a-z]*$".asRegExp()expression
assert s1.matchesRegExp(r)computed value is trueexpected value pass
assert s2.matchesRegExp(r)computed value is falseexpected value pass
end test
Although it is recommended that literal regular expressions are written with the /../ delimiters, the ability to convert a string allows you to input a regular expression into a running program.
Bitwise functions
These functions take in an integer value, and manipulate the bit representation of that value.
- bitAnd
- bitOr
- bitNot
- bitXorPerforms an exclusive OR operation on the bit
- bitShiftL - the second argument specifies how many bits to shift-left by
- bitShiftR - the second argument specifies how many bits to shift-right by
Examples of the bitwise functions being tested:
+test
variable aname set to 13expression
assert acomputed value is 0xdexpected value pass
assert acomputed value is 0b1101expected value pass
assert a.asBinary()computed value is "1101"expected value pass
variable bname set to 30expression
assert bcomputed value is 0b11110expected value pass
assert bitAnd(a, b)computed value is 0b1100expected value pass
variable aobname set to bitOr(a, b)expression
assert aobcomputed value is 0b11111expected value pass
variable axbname set to bitXor(a, b)expression
assert axbcomputed value is 0b10011expected value pass
variable notaname set to bitNot(a)expression
assert notacomputed value is -14expected value pass
variable aLname set to bitShiftL(a, 2)expression
assert aLcomputed value is 0b110100expected value pass
assert bitShiftR(a, 2)computed value is 0b11expected value pass
end test
The result of bitNot(a) being -14 , when a is 13, might be a surprise. But this is because the bitwise functions assume that the arguments are represented as 32-bit signed binary integers. So 13 is represented as 00000000000000000000000000001101, and applying bitAnd gives 11111111111111111111111111110010 which is the value -14 in signed two’s complement format, the left-most bit being the sign (0 positive, 1 negative).
Standalone procedures
All procedures are accessed via a call statement.
pause
pause
is used to slow down the execution of a program e.g. for a game. The argument provided to pause is in milliseconds, so pause(100) delays execution for one tenth of a second.
clearPrintedText
clearPrintedText()
clearKeyBuffer
clearKeyBuffer()
printLine
printLine(arg as String) prints the argument followed by a new line.
The primary purpose of this is so that you may choose to do all printing via methods rather than mixing them in with print statements.
Note that the argument must be provided as a String.
printNoLine
printNoLine(arg as String) does not automatically add a newline at the end,
so you may subsequently print something else on the same line (unless you choose to include a newline \n within the string).
Note that the argument must be provided as a String.
printTab
printTab(tabPosition as Int, arg as String)
helps in the layout of information printed to the console, in particular, when printing columns of data. printTab requires an additional argument specifying the tab position which is number of characters from the left of the display. For example:
Note that the argument must be provided as a String.
+main
call printTabprocedureName(0, "Number"position, text)
call printTabprocedureName(10, "Square"position, text)
call printTabprocedureName(20, "Cube\n"position, text)
+for ivariableName from 1expression to 10expression step 1expression
call printTabprocedureName(0, i.asString()position, text)
call printTabprocedureName(10, "{i^2}"position, text)
call printTabprocedureName(20, "{i^3}\n"position, text)
end for
end main
Right-align numeric output using a lambda function:
+main
variable tabname set to 10expression
variable iname set to 0expression
+for ivariableName from 1expression to (tab - 1)expression step 1expression
variable jname set to 9^iexpression
variable fname set to lambda j as Int => j.asString().length()expression
call printTabprocedureName(tab - f(j), "{j}\n"arguments)
end for
end main
System methods
System methods appear to work like functions, because:
- they may require one or more arguments to be provided
- they always return a value
- they are used in expressions
However, system methods are not pure functions because:
- They may have a dependency on data that is not provided as an argument
- They may generate side-effects, such as changing the screen display, or writing to a file
Because of these properties, system methods may be used only within the main routine or within a procedure. System methods may not be used inside a function that you have defined, because to do so would mean that your function would not be pure.
System methods are all defined within the Elan standard library. You cannot write a system method yourself.
System methods are commonly associated with Input/Output, but note that:
- Input/output may also be performed via procedures
- Some system methods do not appear to be concerned with input/output: see the list below
- The reason those are system methods is that they have a dependency on variable data that is not passed into them as arguments
clock
clock Returns an integer that increments every millisecond. It is useful for measuring elapsed time by comparing the values returned by two such evaluations of the clock method.
getKey
getKey()
getKeyWithModifier
getKeyWithModifier()
inputString(prompt as string)
inputStringWithLimits(prompt as string, minLength as Int, maxLength as Int)
inputStringFromOptions(prompt as String, options as List)
inputInt(prompt as string)
inputIntBetween(prompt as string, min as Int, max as Int)
inputFloat(prompt as string)
inputFloatBetween(prompt as string, min as Float, max as Float)
openFileForReading
openFileForReading: see Reading textual data from a file
random
random and randomInt: see Generating random numbers
waitForAnyKey
waitForAnyKey()
Library functions that process Lists
max and min
Both these functions may be applied to an ListImmutable<of Float> and return the maximum or minimum value found therein.
variable a set to {33, 4, 0,99, 82, 55}
print "Max: {a.max()} Min: {a.min()}"
Higher order functions (HoFs)
These dot methods are called on any List, ListImmutable or String. As ‘higher order functions’ they take either a lambda or a function reference as one of their arguments: see Passing a function as a reference.
Important: Several of these methods return a ListImmutable but this may be converted to an array using .asList() at the end of the expression.
These are not yet fully documented but, for readers familiar with HoFs from another programming language, some examples are shown below.
filter
Usage:
let matches be rules.filter(lambda r as Rule =>
(r.currentState is currentState) and (r.currentSymbol is tape[headPosition]))
map
Usage:
let next be cellRange.map(lambda n as Int => nextCellValue(cells, n))
reduce
Usage:
let d2 be possibleAnswers.reduce(d,
lambda dd as Dictionary<of String, Int>, possAnswer as String =>
incrementCount(dd, possAnswer, attempt))
maxBy and minBy
Alternative implementations of max and min:
variable a set to {33, 4, 0,99, 82, 55}
print a.maxBy(lambda x as Int => x mod 10)
sortBy
sortBy takes a lambda that takes two arguments (of the same Type as that of the ListImmutable being sorted) and compares them, returning an integer with one of the values -1, 0, or +1, to indicate whether the first argument should be placed respectively before, adjacent to or after the second argument in the sorted result, where ‘adjacent to’ means it does not matter whether before or after):
variable source set to {11, 3, 7, 37, 17, 2, 19, 5, 23, 27, 31, 13}
print source.sortBy(lambda x as Int, y as Int => if x > y then 1 else -1).asImmutableList()
The following are not HoFs, but are included here because they are most likely to be used with one of the HoFs listed above.
range
range(first as Int, last as Int) as ListImmutable<of Int>
returns a ListImmutable containing all the integer values between the two argument values.
Dot methods that work on many different Types
.asString()
.length()
.head() returns the first item in a List or a ListImmutable
Empty method table template
Method | Returns | Parameters | Description |
Functions |
asString |
String |
as |
|
Procedures |
|
|
as |
|
Elan Library Reference go to the top