Nemerle for OOP Programmers Week 0 - vilinski/nemerle GitHub Wiki

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.

Table of Contents

Values and Variables

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.

Type Inference

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;
you write:
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.

Methods

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.

Returning Values

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
}
So the function's return type should agree with the type of last expression in its body. If it does not yield any value, then the function does not return anything (and its return type is called void).

Primitive Types

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.

Simple 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);
You can save these two lines into a file, say 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);

Decision Statements

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);
This code will show True or False depending on the input from the user.

Combining relational operators

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.

Using conditions

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 ()
is exactly the same as:
when (!something)
  DoSomethingElse ()
Whether or not you use 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 ().

Code blocks

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).

Exercises

The homework for this week is very easy to do. The intention is that the hardest part is the Nemerle installation.

Install Nemerle

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

Exercise 1

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).

Exercise 2

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.

ToC | Week 1

⚠️ **GitHub.com Fallback** ⚠️