Tuesday, January 31, 2006 

C#: in Search of Syntax

I mentioned the "library design is language design" concept in my previous post. In these days, I've been looking back at a library I wrote for C#. A library is a small language for those using it, and C# (Java is even worse) just don't allow a nice, natural syntax. In my library, I've a concept of Rule. A rule is made by a potentially complex predicate and an action. Whenever the predicate changes to true, the action is triggered. Predicate and Action are a base classes for a potentially large number of predicates and actions, some provided in the library, some user-defined.
Now, a sensible syntax to add a rule could be:
ruleSet.AddRule( Predicate, Action ) ;
where Predicate is a simple predicate like
IsChecked( box1 )
or a "composite" predicate like
IsChecked( box1 ) && ! IsChecked( box2 )
Now, C# helps by providing [a limited form of] operator overloading. But unfortunately, the code above is not legal C# if IsChecked is a class name (as it wants to be). Indeed, C# does not help in providing a good, natural syntax when creating libraries. Callers are required to put all those useless "new" around to create objects (when you can't have objects on the stack Vs. heap, "new" becomes semantically useless), disrupting the natural reading of code.
There are a few ways around this, but so far no one is really satisfactory. The best syntax I've found is creatively based on generics :-), but requires the caller to derive from a concrete base class. Therefore, thanks to the lack of multiple inheritance :-), it can't be used, as the caller is most likely already derived from Form or UserControl. I guess I need some creative approach I haven't found yet. Any ideas? :-)

Sunday, January 29, 2006 

Something new from Bjarne

I have some reflections on good old AT&T proverb "library design is language design", but it's getting late (though it's raining out here). So meanwhile, this will make for a nice (and unrelated) read on the subject: A rationale for semantically enhanced library languages from Bjarne Stroustrup. The man is still the man :-).
By the way, the "Safe C++" example seems directly inspired by my old interview with Bjarne. Ok, ok, I'm not serious about that :-).

Know why and proper examples

As a consultant and teacher, I meet lot of people. Some of them [a small minority] don't even seem to know how to do things. Most of them know how. A subset know why things have to be done in a certain way.
"Know why" is difficult to achieve. One of the reasons is that books, papers and trainers often provide toy examples, from which the reader / student can easily learn how, but not why. They then move to other subjects, because many people just don't care enough to learn the why part.
Consider the concept of iterator in C# 2.0. It's bad enough that they used the wrong name: "iterator" is a widely known concept in C++, and it's even a pattern in the GoF book, and what we usually mean by iterator is much closer to what is called an enumerator in C# than by iterator. Still, their concept is quite cool. However, they completely waste it in almost every example I've seen. They invariably start by repeating the "C# Version 2.0 Specification": Generally, enumerators are difficult to implement, but the task is significantly simplified with iterators. Then they provide a trivial example (a stack iterator, or a constant list iterator), where writing the enumerator would have been trivial as well.
The true power of C# iterators can't be seen on toy problems. You have to step up a little, like defining a Tree class and (e.g.) a depth-first iterator. Here you'll see a significant difference in complexity between an enumerator and an iterator, and you'll understand why. The enumerator has to keep all the navigation state within its own data members, and therefore has to make that state explicit. The iterator does not need any explicit navigation state. It's all implicit in the stack. The iterator can even be made recursive, as you need to navigate a tree (you need a little bit of creativity for this: try it out!). Indeed, writing a depth-first tree iterator in C++ is not trivial, but it's extremely simple with the yield return paradigm of iterators in C#.
Try out this simple exercise, and you'll know why :-).

Saturday, January 28, 2006 

Nerds, Commodities and Business skills (again)

In September 2005, I posted a somewhat controversial piece, The Nerd as a Commodity. A torrent of private email followed, together with a few animated :-) discussions in real life. I planned on writing more on the subject, but instead (or before that) I'll post a few links here, where similar arguments are discussed. A large part is (mostly) about the need for the [software] engineers to drop the geeky habits and learn some [real] business skills:
Engineering makeover seeks image upgrade
A Techie, Absolutely, And More
Beyond Academics
Staying on top in the global IT job market
Catching the Brass Ring (abstract only, requires subscription)
There is, of course, the other side of the story. Some companies, like Google, are fostering the geeky attitude:
Revenge of the Nerds -- Again
Dunno about you, but I find the "ZANY CULTURE" paragraph all so sad. Speaking of which, here is an interesting piece:
The engineers are feeling gloomy
I'll leave the obvious reference with my post above as the dreaded exercise for the reader :-).

Friday, January 27, 2006 

Disposal and Finalization in .NET

I'm updating the material for my Industrial Strength Programming in C# and Introduzione alla Piattaforma .NET courses, adding some material on Finalization, Disposal and SafeHandle. For those not lucky enough :-))) to follow my courses, I guess the best reference available on the subject is DG Update: Dispose, Finalization, and Resource Management from Joe Duffy. There are a lot of non-trivial details about Disposal and Finalization, including the fact that during disposal you have to handle circular references by yourself (fortunately, it's pretty easy), and that if you don't suppress finalization explicitly, you'll incur some serious overhead as your objects get promoted to the next generation (for a practical and useful demonstration, see Introduction to the CLR Profiler by Peter Sollich). What's usually missing in many books and papers is why objects with a finalizer get promoted to the next generation: it would be entirely possible to finalize them during the collection process itself. The answer is pretty simple (in fact, almost simplistic): GC is meant to be fast, and finalization would slow it down. However, (hence the "simplistic") promoting objects to the next generation may force a generation 1 or 2 GC in cases where a generation 0 collection would be enough, had finalization been carried out. By careful following the Dispose(void) / Dispose(bool) pattern, we can make our classes good CLR citizens, so that the GC won't promote objects without necessity. There is still some complexity in implementing disposal right, and it's easy to make some mistake and end up with a resource leak, especially during maintenance. Well, that's what you get when you enter the garbage collected world :-).

Sunday, January 22, 2006 

Functional Fixedness and Einstellung

A few days ago, while teaching Object Oriented Design, I introduced the idea of initiative as defined by Andrew Koenig in "Turning an interface inside out", Journal of Object Oriented Programming, May 1997.
Koenig shows how turning an algorithm into an iterator can make it more reusable and even more efficient in several cases.
The idea is pretty simple in retrospective, but requires a break from traditional thinking. We usually implement an algorithm as a function; we are actually "trained" to do so.
In Gestalt Psychology, this is known to cause an Einstellung effect, whereby after repetitive training we tend to [automatically] use the same approach to problem solving, even though better approaches could be [easily] found for specific problems.
A similar (although distinct) effect is that of Functional Fixedness: in several experiments, human subjects didn't see an opportunity to reuse some artifact with a different function, if a primary function was suggested in the problem setting. For instance, a box that is provided full of objects (therefore suggesting a containment function) is frequently not reused with a different purpose, while an empty box is more frequently reused.
Functional Fixedness appears in programming too, as we seldom reuse language constructs for different functions. Template Metaprogramming in C++ is a great example of what we could get by breaking away from functional fixedness (in this case, templates are used to do compile-time calculations, instead of "simply" providing a way to create type safe algorithms and containers).
It would be fun to list down common concepts in modern programming languages (types, functions, etc.), their "primary function" (structuring, computation, etc.) and then look for any chance of creatively reusing language constructs (like: using a delegate for structuring or storage, etc.). Maybe another useful technique like template metaprogramming is sitting there, and we just need to look from the right perspective :-).
For those of us involved in requirements analysis and software design, the effects known in Gestalt Psychology as self-limitation and parasite implication are also quite interesting, but I'll save them for another time...
[I actually took a diversion while teaching, and talked about the stuff above for a few minutes; interestingly, nobody looked at me like I was crazy :-), or if they did, I didn't notice :-)].

Friday, January 13, 2006 

Frameworks

I visited an old customer for 3 days this week, after about a year and a half. Our last common effort was the design of a small, custom framework for data acquisition, process control, and a few other common tasks in industrial automation. We designed everything together, and they implemented two slightly different versions, aimed at different product families, one in C++, one in VB. I didn't do any coding, except a somewhat unusual graph algorithm that I implemented in C++ on the fly :-) to make sure we were on the right track.
Overall, I'm happy with what I've seen. One of the engineers told me the framework "saved the day" in more than one big project. There is still room for improvement, but I guess we'll postpone all that for the next version, based on .NET.
There is always some kind of risk involved when you develop a framework. Statistically, the biggest risk is that the framework will not be used. My simple strategies to mitigate that risk include:
- Develop the core of the framework as part of real applications, then invest some effort to make it application-independent. Make sure you're solving real problems.
- Keep the framework small. If the scope is large, create several mini-frameworks. Keep the frameworks independent whenever possible, while (ideally) easy to integrate. Nobody wants to learn yet another huge framework.
- Involve future users (programmers) as much as possible. They will contribute ideas, learn the rationale behind the framework, and adopt it more readily.

Sunday, January 08, 2006 

Gestalt

I'm reading a classic on Gestalt Psychology and Problem Solving ("Productive Thinking", M. Wertheimer, 1945). It has been written long before programming could even be considered under a psychological point of view, but it's extremely interesting from a design / programming perspective. So interesting that this morning (during a slow breakfast :-) I drafted a couple of pages on how this could lead to a better understanding of what we do while we design. Seems like the beginning of a new short paper :-).

Friday, January 06, 2006 

Article in IEEE Software

My article "Listen to Your Tools and Materials" (tentative title :-) has been accepted for publication in IEEE Software, and will be published in the next few months. The article deals with the notion of design as a reflective conversation with materials, as introduced in the '80s by Donald Schön (Professor Emeritus of Urban Design at MIT).
Schön spent lot of time studying performing professionals, and although his ideas were not rooted in software, I think they have deep implications for the practice of software engineering, as well as interesting suggestions on how we should design software engineering tools, notations, and so on.
Most likely, I'll be able to publish a pre-publication draft on my website.

Sunday, January 01, 2006 

Unifying Concepts

While looking at some AOP Patterns today, a story came back to my mind. Unfortunately I can't seem to remember where I've read it, or heard of it. Maybe some of you guys can help me here :-).
It's about a martial art master, and he says something like: "Years ago, I discovered there were only three basic techniques, and I focused my practice on those techniques. By doing so, I realized there were only two basic techniques, and kept practicing those. Finally, I realized there is only one basic technique."
In software design, we have many principles and techniques, and even different paradigms, but when you practice them long enough, you start to see some sort of basic, unifying concept.
Consider AOP: AOP is built around the principle of Separation Of Concerns. When you apply AOP techniques, you move each crosscutting concern into a self-contained unit, and away from the remaining code.
Consider design for extendibility: when you design for extendibility (e.g. through use of inheritance as a technique) you provide hot spots in your code, so that new functionalities can be added by adding new, self-contained units, instead of changing existing units (which won't then be, by definition, self-contained).
Information hiding is another way to structure software in self-contained units, this time by looking for stable services, shielding other parts from internal details, which is what makes the unit self-contained (or, using the OO terminology, encapsulated).
The list goes on: low coupling / high cohesion, stepwise refinement, divide and conquer, structured programming, functional decomposition, all providing guidance for the single, fundamental act of software design: partitioning knowledge.
As software is not shaped by physical constraints, we are free to impose any kind of structure (including very cahotic structures) on our creations. Partitioning in self-contained units is the ultimate goal of most [all?] design paradigms, principles, and techniques. This seems largely a consequence of the way the human mind works. There is probably something deeper about this, but I haven't mastered it yet :-).
BTW guys: Happy New Year !!!