RSS Feed

Beyond VB and C#

Posted on Sunday, March 1, 2009 in Features

by Ted Neward

ted_neward Within the .NET community, a low-grade battle rages between two groups, those who use Visual Basic and those who use C#, and never has the world of Computer Science seen a more virulent and angry battle… except maybe for those who argue where the curly-braces should go in C++ and C# code. Not that there’s really anything to argue about, anyway; as any right-thinking C# developer knows, they go on the next line. But that’s not the point of this article, so we’ll move on.

(Besides, I’m right, anyway.)

The problem, of course, is that a viewpoint that limits itself to just those two languages misses out on a much larger world, one which contains a much larger set of tools than just those two languages. And while those two languages, like other object-oriented languages before them, are each genuinely useful languages, the unfortunate fact of each is that they are, at heart, just object-oriented languages. And while that may hardly seem to be a limiting factor, considering how much we’ve been able to accomplish with those languages to date, much of that viewpoint stems from the fact that most .NET developers have never seen anything else.

You know the old saying: When all you have is a hammer….

Back in 1999 and 2000, during the incubation before Microsoft’s first .NET PDC, a curious thing took shape. Microsoft invited a number of researchers and academics to participate in a project called "Project 7": as part of its efforts to ensure that the CLR was not just another proprietary technology, Microsoft wanted to see more than just its own efforts running on this new platform. The CLR was at the heart of it, certainly, but the langauges that developers used would be of far more lasting concern; while Microsoft was reasonably certain that its two lead players (an adaptation of its classic Visual Basic and the brand-new C#) would be a hit, even back then the lead architects on the CLR had a vision that encompassed a larger viewpoint.

Consider, for example, Microsoft’s most recent entry into the CLR family of languages, F# (a.k.a Visual F#). Like C# and VB, F# has a number of object- oriented features baked into it, but, unlike C# and VB, F# is primarily a functional language. The nomenclature here is deliberate: just as an object- oriented language drives its users to think primarily in terms of objects, a functional language drives its users to think primarily in terms of functions. This means that certain kinds of programming–such as programming that deals with mathematics or computations, like what might be seen in simulations, financial forecasting, or even game mechanics–becomes much easier than in a language where functions are almost an afterthought.

At this point, if you’re like most .NET developers, particularly those who "grew up" on a steady diet of object-oriented languages, trying to see where a functional approach would be non-detrimental, much less beneficial, is probably somewhat hard. After all, "functions" seems to imply "global functions", and didn’t the High Priests of Good Design decry global functions almost two decades ago?

Here’s a simple example, drawn from a blog post by Chris Smith, of the F# team. In it, Chris creates a simple "artillery game", in which two tanks positioned randomly on a flat plain take turns taking pot shots at one another; the challenge is in selecting the correct angle, velocity, and "shot mass" to reach the other tank before they get you.

In itself, this is a fairly simple (yet addictive) game, but the important part of the story is in its basic construction: like many (if not most) F# projects, while parts of the code are made for a functional language like F#, such as the mathematics involved in calculating the shot itself given the user’s inputs, other parts are pretty clearly not functional at all, such as the UI interaction with the Windows Presentation Foundation libraries.

CLR interoperability rides to the rescue. Because the F# language is a CLR-based language, it’s trivial to put the actual "guts" (the logic) of the artillery calculations into F# functions, then call them from C# code directly, or even indirectly via WPF data-bindings (as Chris does in his code).

Of course, the C# fanatic will quickly point out that C# has a number of features that make functional programming easier–anonymous methods, lambda expressions, basic delegate support, and so on. In the same vein, C# can be argued to be a dynamic language like Ruby, so long as the C# developer doesn’t mind programming via the System.Reflection APIs all the time. A vast gulf lies between what a language "can" support, and what a language "natively encourages".

And herein lies the crux of the situation: every language ever designed, from ancient Lisp to modern C#, was designed with particular ideas and concepts in mind. In other words, like every kind of tool designed throughout history, every programming language has a particular usage model in mind when created, and its feature set is carefully crafted around that usage model.

Consider the aforementioned Lisp, for example. While sporting a syntax that only a mother (or a really dedicated programmer) could love, Lisp maintains a very powerful relationship between code and data: that, at the heart of things, code *is* data, and Lisp programs often manipulate code structures every bit as much as they manipulate user-entered data. This allows Lisp programmers to write layers of abstraction on top of abstractions, eventually creating an entirely new layer on top of the programming language itself. (It’s no accident that many of the "domain-specific language" advocates look at Lisp as a shining beacon of things to come.) This means, then, that the business domain can be much more closely expressed in the language, rather than trying to mold the domain into a strictly object-oriented model.

Fortunately, these ideas aren’t lying fallow waiting for developers to come to Lisp to pick them up and play. Thanks to the foresight of those Microsoft lead architects and developers back in the early days of the CLR’s development, a wide range of languages have appeared on top of the CLR (and even more for the CLR’s twin brother, the Java Virtual Machine, thanks to its half-decade greater age), many of which explore these ideas in depth. And because they run on top of the CLR, all of these languages can take advantage of the rich ecosystem of .NET libraries and tools, like the .NET Framework Class Library, ASP.NET, WCF, WPF, Workflow, NHibernate, and so on.

For example, consider the language Boo, described as a "wrist-friendly language for the CLR", derived from the scripting language Python. A dynamic language, Boo ranks alongside IronPython and IronRuby as languages that make certain kinds of tasks far easier to complete because of its essentially scripted nature and lack of need for compile-time type-safety. (In other words, practically speaking, scripting languages make it easier to create higher-level code.) For that reason alone, Boo (and IronPython and IronRuby) merit investigation.

But Boo also provides the ability to "plug in" to the compiler itself, essentially offering developers the chance to extend the language in the same way that Lisp does: by having a look at the structure of the program after it has been parsed, in what compiler wonks refer to as the "abstract syntax tree", or "AST", but before it has been turned into IL. This gives the Boo programmer an opportunity to edit, validate and modify that AST in various ways, providing a clear "meta-programming" capability to the language that C# and VB both lack. In Boo, this is known as a syntactic macro.

For example, a simple macro that comes with the language is the ability to conditionally process certain snippets of code, depending on whether a particular string is provided during the compilation phase. In other words, this is Boo’s equivalent to the #if preprocessor directive from C/C++ or C#. (When Boo is interpreting the code in a more scripting-like fashion, the flag is assumed to be passed in to the Boo engine’s command-line.) Its usage looks pretty simple:

ifdef "BOO": 
        print "woohoo BOO is defined"

print "okthxbye"

and the definition of the "ifdef" macro is remarkably simple as well, considering that we’re hooking into the compilation process itself:

import System
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast

macro ifdef:
        if not ifdef.Arguments[0] isa StringLiteralExpression:
                raise ArgumentException("ifdef argument must be a string literal.")
        if Context.Parameters.Defines.ContainsKey((ifdef.Arguments[0] as StringLiteralExpression).Value):
                return [|
                        $(ifdef.Block)
                |]

Similar macros come predefined with the language, such as the "using" macro, which serves the same basic purpose as the "using" keyword does in C#:

import System 
import Boo.Lang.Compiler 
import Boo.Lang.Compiler.Ast 
     
macro using: 
        expansion = using.Body 
        for expression as Expression in reversed(using.Arguments): 
                temp = ReferenceExpression("__using${_context.AllocIndex()}__") 
                assignment = [| $temp = $expression as System.IDisposable |].withLexicalInfoFrom(expression) 
                     
                expansion = [| 
                        $assignment 
                        try: 
                                $expansion 
                        ensure: 
                                if $temp is not null: 
                                        $temp.Dispose() 
                                        $temp = null 
                |] 
                     
        return expansion 

This ability to extend the core language is a powerful feature, and one that shouldn’t be taken lightly — consider the pain and suffering that Visual Basic developers went through, waiting for the team at Microsoft to introduce the "using" keyword into their language. Had Visual Basic supported a syntactic macro system like Boo’s, developers could have added it themselves, alongside any other interesting and useful macros they’d cooked up along the way. Or, if you don’t particularly like the exact semantics of the "using" keyword–I personally have always wanted a diagnostic logging statement in the "finally" block generated by the compiler–then in a macro-based approach, the semantics can change without having to go back to Microsoft to ask for a patch.

The point of all this, in case you were concerned, is not that you need to give up your existing fondness for an object-oriented language; Lord knows, I’m not going to any time soon, either. C# and Visual Basic are both languages that serve a useful purpose, and nobody should be telling you to give up something that serves a useful purpose. (Though you’ll never get me to admit it, Perl probably does, too, but let’s keep that a secret between us, OK?) In fact, the point is precisely the opposite: to make use of *all* the tools available to you–F#, Boo, Nemerle, C# and/or VB, as the problem dictates.

After all, who wants to own just a hammer? Sounds boring, to me.

Ted Neward is a Principal Consultant with ThoughtWorks, an international consultancy developing agile solutions. He is fluent in Java, C#, Scala, F#, C++, and a few other languages, and spends a lot of time studying programming languages, virtual execution engines, and scalable enterprise systems. He currently resides in the Pacific Northwest.

Be the first to comment.

Leave a Reply

`

Bad Behavior has blocked 240 access attempts in the last 7 days.