Functional programming
Elan is designed to support the ‘functional programming’ paradigm.
Unlike in most ‘mixed-paradigm’ programming languages, all functions in Elan are ‘pure functions’. Elan does not permit any function to create ‘side-effects’, and enforces that the returned value is derived solely, and deterministically, from the values passed into the function’s parameters. This applies whether or not you are actively seeking to write code according to the functional programming approach.
When writing code according to the functional programming paradigm the aims are:
to write as much as possible of the program’s logic and behaviour within pure functions
to use the Main routine and Function and Procedure calls solely for implementing input/output
to keep both main and procedures ‘as thin as possible’.
Elan’s built-in support for character-mapped BlockGraphics is a good example of this pattern: almost all the work can be done using the built-in functions, such as withBlock, which may be used within your own user-defined functions. Only the draw method (the only one that actually changes the display) is a procedure, and this must be called from within main or a procedure.
Although it is not a requirement to do so, adopting the functional programming paradigm also means that, wherever possible, functions should avoid using procedural code constructs: sequence, loop, and branch. Here are some examples of functions that don’t use any of those procedural code constructs:
function w(c as Int) returns Int
return if (c mod 40) > 0 then c - 1 else c + 39
end function
function possibleAnswersAfterAttempt(prior as List<of String>, attempt as String, mark as String) returns List<of String>
return prior.filter(lambda w as String => markAttempt(attempt, w) is mark).asList()
end function
function nextGeneration(cells as [Boolean]) returns [Boolean]
let cellRange be range(0, cells.length() - 1)
let next be cellRange.map(lambda n as Int => nextCellValue(cells, n))
return next.asArray()
end function
In the examples above we can see several patterns or techniques that are widely used in functional programming in place of procedural code constructs:
(Top example) Use of an If expression, instead of using an If statement.
(Middle example) Use of Higher order functions – in this case, filter – together with Passing a function as a reference.
If you are passing a reference to a free-standing function as an argument into a HoF (as distinct from defining a lambda) then you provide the name of that function, but precede it with the keyword function. For example:
variable passes set to allPupils.filter(function passedMathsTest)
function passedMathsTest(p as Pupil) as Boolean
return p.mathsPercent > 35
end function
Notes
When passing in the reference function passMathsTest, the name is preceded by function, and no parameters (or brackets) are added to the name as they would have been if you were evaluating (calling) the function at that point.
lambda, instead of writing a loop.
Use of a let statement (instead of a variable statement) to calculate intermediate values.
These are explained below.
Notes
When the if expression is being entered or edited, it is presented as a single editable line which may be scrolled horizontally. As soon as the entry/edit field is exited, the expression will be re-formatted as shown above, with each else clause starting on a new line, indented by one character. Note that each of the three examples above is still a single statement and not a sequence of statements.
The else keyword may be followed by a further if expression, as in the second example above. It is possible to form a long ‘chain’ of these else if clauses, in which case there is only ever one unconditional else clause at the very end.
If you wish to have another if expression within the then clause then this ‘nested’ if expression must be surrounded by brackets in order to avoid ambiguity. However, nesting if expressions in this way, rather than chaining else if statements (above), will make the if expressions hard to read and interpret.
Higher order functions (HoFs)
A ‘higher order function’ is one that takes in a reference to another function as a parameter, or (less commonly) that returns a reference to another function as its result.
Standard HoFs
The Elan standard library contains several HoFs that are widely recognised and used within functional programming. See Higher order functions (HoFs).
Passing a function as a reference
On most occasions when you write the name of an existing function elsewhere in code your intent is to evaluate the function and, to do so, you write the name of the function followed by brackets containing such arguments as are required by the function. For this reason if you forget to add the brackets, you will get an error, for example:

The second sentence in this error message is for when your intention is not to evaluate the function, but to create a reference to the function. This is a valid thing to do in functional programming but is not generally done in procedural programming. As the error message says, to create a reference to a function you need to precede it by and the name of the function should then not be followed by brackets (or any arguments). For example:
variable passes set to allPupils.filter(ref passedMathsTest)
function passedMathsTest(p as Pupil) as Boolean
return p.mathsPercent > 35
end function
Defining your own HoFs
The Func Type
Generating random numbers within a function
It is not possible to use the system methods random() or randomInt() within a function because they create unseen side-effects. You may use those system methods outside the function and pass the resulting random number (as an Int or a Float) as an argument into a function.
It is possible to create and use random numbers within a function, but it requires a different approach and is a little more complex, using a special Type named Random (note that the R is in upper-case), as in this example:
main
variable rnd set to new Random()
call rnd.initialiseFromClock()
variable dice set to 0
for i from 1 to 10 step 1
set dice, rnd to rollDice(rnd)
print dice
end for
end main
function rollDice(rnd as Random) returns (Int, Random)
return rnd.nextInt(1, 6)
end function
The Random Type defined two ‘function methods’: next and nextInt.
Both of them return a 2-Tuple consisting of the random value (as either a Float or an Int respectively) plus a new Random. The new (returned) Random must be used for generating the subsequent random number (if more are required). If you call next repeatedly on the same instance of Random, you will always get the same value.
As shown in the example, when first created you should call initialiseWithClock() on it. If you remove that call statement from the code above, the program will still generate a sequence of randomised values, but the sequence will be exactly the same each time you run the program. Initialising from the clock ensures that you get a different sequence each run. Using Random without so initialising, however, can be extremely useful for testing purposes since the results are repeatable.