-
Notifications
You must be signed in to change notification settings - Fork 89
Nemerle for OOP Programmers Week 0
In this first lesson, we will get in touch with Nemerle syntax, and some of the basic features of the language. At the end, we will also learn how to install and configure a usable Nemerle compiler.
We assume that you have general knowledge about all the items that are treated here, so we won't dig so much into them.
Just as a review: a value is a place in memory that holds some data. Each value has an identifier, or name, which allows us to refer to its contents, and operate on it. In most languages, identifiers declared in code are read-write variables, but this is not the default scheme in Nemerle. Indeed, there are two kinds of identifiers: mutable and immutable.
Immutable identifiers (values), far more commonly used in Nemerle, are defined with the following syntax. There's always an initializer, because if there wasn't, we wouldn't be able to use it at all:
def name : type = value
For mutable identifiers (variables) we just change the def
keyword to
mutable
, like this:
mutable name : type = value
Immutable values are typically used for naming some object, and using that same instance for the life of the value. Mutable variables are used as place holders for some data which is meant to be changed in further computation. You will see that such a distinction leads to a different approach to solving programming problems.
You might have wondered why we use a special keyword for declaring
values, instead of just writing type name;
as in C. This is
because the type can be skipped in most cases. So instead of writing:
def x : int = 42;
def x = 42;
This is not limited to literals, as you can also write something like:
mutable y = null;
// do something
y = SomeComplexObject ();
This is still OK.
As you surely know, a method or function is a group of statements, executed sequentially, which are given a meaningful name. Optionally, methods can have a return value as the final value of the computation. Methods can also have parameters, that is, values that are given to the method so it can know what to do. The general syntax for methods is:
name ( parameter1 : type, parameter2 : type ... ) : return_type
As you can see, the only difference when compared against C#, C++ or Java is that types are written after the parameter name, instead of before them. This is just a matter of convention; Visual Basic, for example, also places it after the name.
NOTE: type inference is not available for parameters and return types of methods. Having type inference for public methods could lead to accidently changing exported interfaces of classes — which is Not Good™. We plan to implement it for private methods though.
Continuing in the functional programming tradition, Nemerle doesn't use return or any other keyword to specify which value a function gives back as it's result. This fits very well in Nemerle: there are no goto, break or label statements that could disrupt normal program flow (well, there are some macros extending the language to allow them, but for now let us just ignore them).
The question then arises: if there is no keyword, how does the compiler know which value to return? The answer is very easy: the last value that has been computed. The easiest example to understand this is:
add (number1 : int, number2 : int) : int {
number1 + number2
}
Well, up to this point we have used the word type lots of times, but we have not really defined it. Type is a fundamental concept in Object Oriented Programming. It represents an abstract idea of what a value is. For now, we will only introduce the primitive types:
Short name | Full name | Description |
---|---|---|
sbyte |
System.SByte |
A signed 8-bit wide integer. |
short |
System.Int16 |
A signed 16-bit wide integer. |
int |
System.Int32 |
A signed 32-bit wide integer. |
long |
System.Int64 |
A signed 64-bit wide integer. |
byte |
System.Byte |
An unsigned 8-bit wide integer. |
ushort |
System.UInt16 |
An unsigned 16-bit wide integer. |
uint |
System.UInt32 |
An unsigned 32-bit wide integer. |
ulong |
System.UInt64 |
An unsigned 64-bit wide integer. |
string |
System.String |
An immutable string of characters. |
char |
System.Char |
A character, represented internally by a 16-bit integer. |
void |
System.Void |
A type with exactly one value, named () in Nemerle.
Used when a method doesn't return anything sensible. |
float |
System.Single |
A 32-bit IEEE floating point number. |
double |
System.Double |
A 64-bit IEEE floating point number. |
decimal |
System.Decimal |
A type used for money representation. |
During this course you probably won't touch anything beside int
,
string
, char
and void
, so don't bother remembering
the above :-)
The Full Name is the name used in the Base Class Libraries (BCL), which you can use to lookup information in the .NET Framework SDK Documentation, or similar reference. In code it is easier to use the short name, but the longer one is equally valid. They are interchangeable.
Nemerle is based on Microsoft .NET Framework, or its free implementation, Mono. That means that these types are not really just for Nemerle, but are common for any language targeting the .NET Framework. You have to adapt to this new platform, because while the syntactical parts of the language come from Nemerle, the practical parts (interaction with the user, communication over a network) come from the BCL. If you come from C#, you very likely know the Base Class Libraries well already. If you are new to .NET, you will need an introduction, so we will start with some working examples.
For example, if I wanted to ask the user for her/his name and present a greeting, I would write a program something like this:
def name = System.Console.ReadLine ();
System.Console.WriteLine ("Hello " + name);
greeting.n
. Compile it
using:
ncc greeting.n
and then run it (at the Windows cmd prompt):
out
(or on Linux with mono, in a terminal window):
mono out.exe
Nemerle does not require you to provide any classes or methods, you can just throw expressions to evaluate into a file and it will run it.
Another way to start a program's execution is to write a static method
called Main
in some class. So here, the example would be:
class Greeter {
public static Main () : void
{
def name = System.Console.ReadLine ();
System.Console.WriteLine ("Hello " + name);
}
}
You cannot mix these two techniques, or for that matter, place top-level code in more than one file. What you can do is define some classes before (but not in the middle, or after) the actual code to be executed. This is what we will do in this course, as it is more concise.
So the most sophisticated way to write this simple example would be:
class Asker {
public static Run () : string
{
System.Console.ReadLine ()
}
}
def name = Asker.Run ();
System.Console.WriteLine ("Hello " + name);
One of the main functionalities in every programming language is the ability to execute instructions depending on some values or some input from the user. If you know some other language, this should be a familiar concept. The relational operators in Nemerle are the ones found in C#, C++ and Java:
== |
Equal to |
!= |
Not equal to |
> |
Greater than |
>= |
Greater or equal than |
< |
Less than |
<= |
Less or equal than |
NOTE: other languages, such as Visual Basic or Pascal, use
=
as the logical operator for equal to. This is an assignment
operator in Nemerle, therefore it is an error to use it as a relational
operator.
Results from relational operators can be saved in bool
values,
and these values can be used like any other:
def number = int.Parse (System.Console.ReadLine ());
def isEven = (number % 2) == 0;
System.Console.Write ("The number is even :");
System.Console.Write (isEven);
True
or False
depending on the
input from the user.
Sometimes it is necessary to combine more than one condition. This can be done using the following operators:
! c |
Negation, returns true if c was false , and false if c was true . |
c1 && c2 |
And, checks c1 , if it is false returns false otherwise returns c2 . |
c1 || c2 |
Or, checks c1 , if it is true returns true otherwise returns c2 .
|
Both &&
and ||
are evaluated lazily, which means the
following code:
when (foo != null && foo.Length > 0)
System.Console.WriteLine (foo);
will never yield NullReferenceException
, because the
foo.Length
will not be evaluated if foo != null
was false.
This is the same behavior as in all C-like languages.
Once we have learned how to check for conditions, it's time to learn how to execute a piece of code depending on them. The three main conditional constructs for this task are:
when (condition)
code
when
executes the code only when the condition is true
.
unless (condition)
code
unless
executes the code only when the condition is false
.
if (condition)
code1
else
code2
if..else
executes the first block of code when
the condition is true
, or the second if its result is
false
. On the main C-family languages, the notion of when
or unless clauses does not exist, so they are managed using ifs
without else. In Nemerle, every if has its else.
As you can see:
unless (something)
DoSomethingElse ()
when (!something)
DoSomethingElse ()
unless
is up to you: use what reads
better to you. No styling guidelines here.
In Nemerle, conditional statements are also functions (as in other
functional languages), so the type of the return value of each branch
must be the same. However, these constructs are mostly used as they are
in C# or Java (the type of both branches is void
), so
there should be no problem if you don't miss your final ;
. This
is specially important in when/unless, because, as stated in
Language Reference,
they really translate to an if...else statement, which returns ()
.
The final word of this week are code blocks. Conditional statements support just a single statement written within their body. However, most of the time, you will need to execute more than one statement. This is achieved using code blocks: a series of statements written inside { and }, that are evaluated sequentially, and whose return type is the last value computed (remember, it's a functional language).
The homework for this week is very easy to do. The intention is that the hardest part is the Nemerle installation.
Install the latest stable version of Nemerle in Linux, Windows, MacOS or whatever operating system you use. If you use Linux or MacOS, you will need to install Mono first. If you use Windows, you will need the latest release of .NET Framework 2.0 (Beta 2 is too old, use at least the August 2005 CTP).
The Step by step guide to get Nemerle compiler running on our Webpage has all the details, including MS.NET/Mono installation instructions.
You can ask any questions on the course mailing list. You can also configure some editors (such as SciTE, VIM, XEmacs, Kate, GEdit, mcedit) for Nemerle syntax highlighting. You can find configuration files in the SVN repository.
Version 0.9.4 works with .NET 3.5 and Visual Studio 2008 using VS2008 integration. If you have no VS2008 installed, you should install it from Visual Studio 2008 Shell (free). See additional info at VS integration project page at RSDN. See last info about downloads at Nemerle Download Page
Create a console application that asks the user for two numbers and then
shows them added, subtracted, multiplied, divided and its modulus. You
can also build upon it a program that shows the sin, cosine and tangent
of a specified angle (look in the System.Math
class).
Add a menu (a list of possible operations and a prompt for a choice) to this program so the user can choose which operation to perform. Implements separate methods for reading numbers and displaying the menu.