Programming language designers and users talk a lot about the “height” of language features; some languages are considered to be very “high level” and some are considered to be very “low level”. A “high level” language is generally speaking one which emphasizes the business concerns of the program, and a low-level language is one that emphasizes the mechanisms of the underlying hardware. As two extreme examples, here’s a program fragment in my favourite high-level language, Inform7:



Overlying relates one thing to various things. The verb to overlie (it overlies, they overlie, it is overlying) implies the overlying relation. The jacket overlies the shirt. The shoes overlie the socks. The slacks overlie the undershorts. The shirt overlies the undershirt. Before wearing something when something (called the impediment) which overlies the noun is worn by the player: try taking off the impediment; if the player is wearing the impediment, stop the action.

This is part of the source code of a game;1 specifically, this is the code that adds a rule to the game that describes what happens when a player attempts to put on a pair of socks while wearing shoes. Note that the function which takes two objects and returns a Boolean indicating whether one overlies the other is not written as a function but rather as a relation associated with a verb, and that the language even allows you to provide a conjugation for a non-standard verb so that you can use it in any natural English form in your program. It is hard to imagine a language getting closer to the business domain.

By contrast, x86 assembler is a very low-level language:

add ebx, eax neg ebx add eax, ebx dec ecx

In one sense we know exactly what this fragment is doing; adding, negating and decrementing the values in registers eax , ebx and ecx . Why? Hard to say. The business domain is nowhere found here; this is all mechanism.

A compiler is a device which translates a program written in one language into an equivalent program in another language; typically the source language is a high-level language and the target language is a low-level language. A common technique along the way though is to have the compiler “lower” from high-level language features to low-level language features in the same language. For example, the C# 3 compiler goes through several stages of lowering when it is working on a query expression:

IEnumerable query = from c in customers where c.City == "London" select c;

is first lowered into this equivalent C# fragment:

IEnumerable query = customers.Where(c=>c.City == "London");

which is then lowered to

IEnumerable query = Enumerable.Where(customers, c=>c.City == "London")

and then

static bool Lambda(Customer c) { return c.City == "London"; } static Func lambda; ... if (lambda == null) lambda = Lambda; IEnumerable query = Enumerable.Where(customers, lambda);

And then the conversion from method group Lambda can be lowered into a call to Delegate.Create and so on.

You can think of there being a “proto-C#” language which has no async methods, query comprehensions, extension methods, using statements, for loops, iterator blocks, lambdas, lifted operators, and so on; during compilation the compiler lowers C# program into this “proto-C#” language which can then be more easily transformed into IL.

Next time on FAIC I’ll discuss a principle of language design that C# does not cleave to, and discuss how some of the proposed new features for C# 6 bring C# more in line with that principle.

1. When In Rome 1: Accounting For Taste by Emily Short.