Tuesday, January 31, 2006
C#: in Search of Syntax
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
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
"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)
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
Sunday, January 22, 2006
Functional Fixedness and Einstellung
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
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
Friday, January 06, 2006
Article in IEEE Software
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
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 !!!





