To start programming with Elan, first familiarise yourself with using the Elan IDE, especially for
entering and editing code. Do this by watching the videos here .
You will learn that Elan supports programming in Python, VB, C#, and Java
(as well as the Reference Language Elan),
and offers the option of immediate translation between these languages.
If you have previously programmed in any of the languages you will find that Elan enforces
best practice by means of the following constraints on how code is written.
There are three forms of constraint, which are described in more detail in these sections:
Best practices in coding that Elan encourages
Elan supports, and in many cases enforces good programming practices, which
are automatically applied across all the supported languages, even where
a language ( when used outside Elan) would allow you to do otherwise. In the context of procedural programming
(the procedural paradigm having been selected in the IDE), there are seven such best practices,
some widely recognised, others less so.
Some programmers will argue that there are specific circumstances where
breaking the rules should be acceptable. The problem with this 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 to learn to program without breaking these rules than that to adopt
bad habits from the beginning. This will result in you writing better code and encountering fewer unexpected problems.
The six best practices enforced by Elan for procedural programming are:
- No global variables
- Static typing
- No null references
- No breaking out of loops before they have completed
- Return from a function or procedure only after the final instruction
- All functions must be pure
together with this seventh best practice that is supported and encouraged by Elan:
- Every function should be unit tested
No global variables
It is widely recognised that global variables are a bad idea: they encourage poor program structure and lead to unexpected
behaviour. Although most programming languages permit them, the Elan editor solves this by offering the variable definition
instruction only within the main routine, functions, and 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 to adhere to this rule,
and all code must be within this. Everything added immediately within this class is deemed, in Elan terms, to be
global, so you will not be able to add a variable, even in the form of a class property there.
Static typing
Static typing means that the type of every variable, 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 statically typed. but Python is inherently dynamically typed.
Python 3.5 offers the option to add 'type hints' that assist 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 purely educational context, using static typing gives a stronger
understanding that, in all modern languages (with static or dynamic types) all data items always have an associated type, and static typing helps
you to understand the nature of those types.
When entering and editing Python, Elan therefore enforces the use of type hints.
Elan does not require the type to be specified explicitly for any variable (or constant) because its type is inferred from the
initial value given to it.
No null references
All programmers have encountered the notorious 'Null Reference ...` (or 'Null Pointer ...`) error message, and the cause 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, though 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 assigned 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 allows you to break out of
a loop before its completion. Elan does not permit any of those instructions to be used for the following reasons.
The break instruction was invented when the only possible kind of loop that a programming language
offered 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.
With properly 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, in that programmers find it convenient to include multiple
returnreturnreturnreturnreturn instructions within a procedure or a function. All the supported languages permit this.
Elan, however, deliberately prevents this practice. An Elan function always has one return instruction as its last
instruction (which specifies the value to be returned), and there is no option to add another one earlier.
An Elan procedure cannot have a return instruction: the procedure exits after its last instruction.
Some programmers may 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 to hold the result. This is true but it's a small price to pay and makes your code more readable
In summary, here are four reasons to avoid early returns:
- You cannot jump into the middle of a procedure or function, nor should you 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 separate breakpoints on, every return statement,
- When you change the requirements of the function – for example so that a returned string is always
upper case – you would need to enforce this in several places, running the risk of overlooking some.
(Some languages offer a 'finally' clause to make that easier, but this is just a kludge to paper over
the cracks of poor structure).
All functions must be pure
Many textbooks draw no distinction between procedures and functions, except that the latter return a value to the code that references it.
Used properly, the two are distinct in other, very important, respects.
A procedure:
- can be thought of as a way of adding you own custom instruction to the language,
- can make a change to the state of the system: which might be by printing a result on the screen, saving data to a file,
or mutating a data structure passed into it, such as sorting a list 'in place' (see the Ripple Sort demo),
- may depend on the system in others ways, for example requesting a response from the user, or reading the system clock,
- must be called directly, never as part of an expression.
By contrast, a proper function:
- can be thought of as a way of adding a custom operator to the language – a way to derive a new value from one or more arguments,
- does not make any change to the state of the system. The only observable effect of referencing a function is the returned
value, which is held, temporarily, on the system 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 where an expression is required, never in isolation (though the function call may constitute the complete expression).
(Referencing a function in isolation – as you would for a procedure – is known as a 'hanging function call' because the returned value is not captured and used).
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, and 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 unforeseen clashes between the side effects of procedures.
- Easier to reason about the correctness of the program, because functions behave more 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 final best practice, as follows.
Every function should be unit tested
Historically, most testing of programs was done by humans, manually running through a large number of 'test scripts' (or 'cases') 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 has shifted to automated testing. Although writing the test scripts or cases still takes time, 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 and use.
Unit tests are intended for testing only proper functions though many languages do not enforce that. Running unit tests on procedures
or on improper functions (that create side effects) can be dangerous – running the tests might result in data being written to a database,
or a network message being sent repeatedly. There are software frameworks for automated testing 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 can benefit from:
- it gives you confidence that code is correct, especially code that you haven't looked at recently,
- it gives you an immediate sense of reward when all the tests pass – by going green,
- 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, and it 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.