Tuesday, June 27, 2006 3:04 PM
by
loufranco
Alternative .NET Languages
I wish Visual Studio had better support for mixing languages in the same assembly or even the same file. It's not because I really like Dim (is there still a ReDim?), but sometimes I want to mix C# and C++, and it would also give me a great way to try
Nemerle and
Boo.
Both languages are aimed at reducing verbosity. Some of their features, like type inferencing and embedded SQL, are coming in C# 3.0, but to me, the killer feature in both is their support for language changing macros. These go way beyond what you can do in C preprocessor macros, and start to approach LISP. Paul Graham sums up why this is important in
Succinctness is Power:
It seems to me that succinctness is what programming languages are
for. Computers would be just as happy to be told what to
do directly in machine language. I think that the main
reason we take the trouble to develop high-level languages is to
get leverage, so that we can say (and more importantly, think)
in 10 lines of a high-level language what would require 1000
lines of machine language. In other words,
the main point of high-level languages is to make source code smaller.
If smaller source code is the purpose of high-level languages, and
the power of something is how well it achieves its purpose, then
the measure of the power of a programming language is how small it
makes your programs.
For all I like about C#, it's really no less succinct from a language perspective than Java for instance. They both get a bum rap because they do reduce overall program size by supplying a well-written and comprehensive standard library. To actually make something I want other people to use on their machines, I'm not willing to stray from C/C++, C#, or Java just yet, as much as Ruby may beckon.
But for algorithm description or sheer object manipulation, the lines of code necessary to do simple things in either of these languages is staggering. Steve Yegge has a
great essay on this topic, where he writes the equivalent Java to this Perl snippet:
my @squares = map { $_ * $_ } (1..5);
print "@squares\n";
It clocks in at about 20 lines of code. The C# version wouldn't be much smaller, because it doesn't address the root problem:
[...]in Java, if you find
yourself saying the same kinds of things over and over again, there's
no way to create shortcuts. All Java 5 does (for compressibility,
anyway) is add a few shortcuts for a few idioms that were especially
aggravating. They didn't help you at all with the more general
problem of creating your own shortcuts.
So, that brings me back to Boo and Nemerle. Both get the benefit of the huge .NET framework, interoperability with your C#/VB/C++/etc classes, plus some 3.0 features, but most importantly, you get macros.
In Boo, you have two ways to get the compiler to write code for you, Syntactic Attributes and Syntactic Macros (good description in the
Boo Manifesto [pdf]). A Syntactic Attribute lets you write this
class Person:
[getter(FirstName)]
_fname as string
Instead of:
class Person:
_fname as string
FirstName as string:
get:
return _fname
Which halves the number of lines, but most importantly reduces the redundancy (and possibility for error). They could have gone even further if _fname was _firstName and the getter didn't take an argument. But since Boo supports macros, I could just do it myself. Then I could make a simple property with just the type and property names:
[getter] _firstName as string
You can write your own Syntactic Attributes by implementing IAstAttribute. Your class will be called at compile-time and be given a chance to manipulate the abstract syntax tree.
Boo's Syntactic Macros let you add entirely new contructs to the language. Boo does not have
using, but you can add it by making a class named UsingMacro that implements Boo.Lang.Compiler.IAstMacro. Your implementation of Expand will be called at compile-time to write the code.
Nemerle's Macros use language constructs instead of interfaces. To define a C style while loop, use
macro while_macro (cond, body)
syntax ("while", "(", cond, ")", body) {
<[
def loop () {
when ($cond) {
$body;
loop ()
}
}
loop ()
]>
}
Nemerle has macros for
design by contract,
embedded SQL, and concurrency. Hey, while I was looking up a good link for concurrency, I found
this -- a new language from Microsoft Research called Cω (pronounced Comega), with some of the same ideas.
C# 3.0 [
doc] is already adding some of the features found in these languages (type inference, lambda expressions, and LINQ), but macros give me the power to add in my own language features, like
PAR and SEQ.