These days retro-minimalism in desktop computing has become quite fashionable. People play old console classics, use minimalistic tiling window managers and many people go back to using text editors that their parents already grew up with.
I have always found this trend a bit weird, especially with respect to text editors. Why on earth should I throw 20 years of CUA, a mousable gui and the convenience of not having to constantly switch between different modes overboard and start to use vi?
I myself learned to program with QuickBasic. Although Windows (I think some version 2.0 or 3.0) *was* running on my computer I did not use it since it was notoriously unstable and stole away precious RAM from my individual-based simulations (yes, this is what I did when I was 16...). Also the DOS IDE QuickBasic came with was actually quite nice. The built-in editor implemented a standard CUA interface, therefore that is what I became used to.
I was accordingly shocked when I started university and had to learn vi. Horrible non-intuitive key bindings, a modal interface, no menus - I was disgusted. Out of necessity I learned enough vi to get along (we weren't even allowed to use X terminals for the first two years), but I never liked it. At home I continued to use QuickBasic and QuickC, or, when I started to switch to DJGPP (it allowed programming for protected mode which meant I could use the whole *8MB* of RAM without jumping through hoops) I just went with the simple DOS edit command and later with rhide.
Later, after switching to Linux I luckily stumbled upon NEdit. NEdit actually made me happy - it was fast, featureful and extremely configurable and scriptable. Unfortunately - being based on Motif - NEdit was also firmly rooted in the ancient past of Unix UI technology. Since there also was no sign of ongoing development I finally with great regret abandoned it (together with WindowMaker and my iBook) a couple of years ago.
Since then I have tried all CUA editors and IDEs I could get a hold on but none of them really satisfied me. Having replaced my iBook with a MacBook I decided to try OS X for a while, which didn't make the text editor situation any easier. I didn't manage to warm up to XCode and Smultron although nice felt a bit too locked in (just in case I wanted to ditch OS X again) therefore I stuck for a while with jEdit. After my annoyance with OS X and my MacBook had grown strong enough to go back to Linux (on a nice Lenovo R61) I decided to give kate and kwrite a try, which I have used since then.
A couple of weeks ago after having encountered another weird annoying bug in kwrite and an extensive round of checking on all linux text editors I know of I finally had enough. I decided to give vi a second chance.
And to my own utter surprise this time around I actually liked it. Vim really excels at configurability, speed, syntax highlighting and multi-window editing (due to its bugginess a constant pain in kate). On the other hand I didn't find it difficult at all to memorize the odd key combinations (though having a cheat sheet pasted to the wall doesn't hurt) and somehow they just didn't feel nearly as unintuitive as during previous encounters. Even the constant switching between insert and command mode which has always been my biggest issue with vi hasn't started to annoy me yet.
I could imagine that it's an age thing. It took me nearly 30 years to start to like olives, capers and anchovy and now I love them (visiting Sicily helped...).
Maybe vi really is an acquired taste and I just needed to pass the 35 to finally learn to appreciate it.
P.S.: While ditching kate/kwrite I also changed (back) to E17 and modified all color schemes to bright on dark. Could be that I'm just following fashion after all...
Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts
Tuesday, 9 November 2010
Sunday, 31 October 2010
D is finally (nearly) there
Last week, in order to avoid working on more important things I started a small project I had wanted to do since quite a while. As mentioned before I am really not happy with C++ as a language to write simulations in. Unfortunately according to the Great Language Shootout all the nice languages are way too slow to be seriously considered.
However - as aptly explained on the shootout page - comparing the speed of programming languages and even the speed of implementations of programming languages is not very meaningful. Results will vary widely dependent on application area.
Therefore the only reasonable thing to do is to implement a benchmark which is representative of the kind of program one is interested in in all candidate languages. Which is exactly what I have started to do.
I have defined a reference model and implemented it in (very very basic) C++ and (C++ish) D (and CUDA which strictly speaking doesn't belong here. More on that in a later blog post - hopefully. I'm really bad at actually writing posts that I have announced before...).
To my great delight it turned out that the D program ran only about 10% longer than my basic C++ version. In principle this is clearly a small enough loss in speed to be compensated for by the nice improvements D offers over C++. I was really disappointed though (you can see how much I would like to abandon C++) when I found out a bit later that PGO (profile guided optimization) gives my C++ program another 20% boost. Add to that the fact that 64-bit D is still some way off and that value types are second class citizens in D and I'm not really sure whether I will do my next project in D or not. In any case it was nice to see the progress they have made. I am optimistic that my C++-days are numbered...
As for the shootout - I will try to find the time to add "proper" implementations in D and C++ (which hopefully will not perform much differently). I hope I will also be able to cover other interesting or up-and-coming languages, such as OCaml or Bitc. If anyone of my three readers feels willing and able to help out - just head over to the project page, read the model definition and hack away. I will be happy to post code/results.
However - as aptly explained on the shootout page - comparing the speed of programming languages and even the speed of implementations of programming languages is not very meaningful. Results will vary widely dependent on application area.
Therefore the only reasonable thing to do is to implement a benchmark which is representative of the kind of program one is interested in in all candidate languages. Which is exactly what I have started to do.
I have defined a reference model and implemented it in (very very basic) C++ and (C++ish) D (and CUDA which strictly speaking doesn't belong here. More on that in a later blog post - hopefully. I'm really bad at actually writing posts that I have announced before...).
To my great delight it turned out that the D program ran only about 10% longer than my basic C++ version. In principle this is clearly a small enough loss in speed to be compensated for by the nice improvements D offers over C++. I was really disappointed though (you can see how much I would like to abandon C++) when I found out a bit later that PGO (profile guided optimization) gives my C++ program another 20% boost. Add to that the fact that 64-bit D is still some way off and that value types are second class citizens in D and I'm not really sure whether I will do my next project in D or not. In any case it was nice to see the progress they have made. I am optimistic that my C++-days are numbered...
As for the shootout - I will try to find the time to add "proper" implementations in D and C++ (which hopefully will not perform much differently). I hope I will also be able to cover other interesting or up-and-coming languages, such as OCaml or Bitc. If anyone of my three readers feels willing and able to help out - just head over to the project page, read the model definition and hack away. I will be happy to post code/results.
Monday, 2 August 2010
Eta, Part II - Syntax (part I)
Many people have pointed out that language designers tend to obsess over syntax far too much and that their time would be better spent thinking about the semantics of their languages. Some (usually those who are either more academically inclined or old lispers) go so far as claiming that syntax is ultimately irrelevant, since a) which syntax someone prefers is largely a matter of taste anyways and b) every syntax becomes 'natural' after sufficient exposure.
Well, this topic has been discussed thoroughly, and I will only add to it to the extent that I am going to justify my own design decisions on the matter.
On the most abstract level a program can be thought of as a nested structure of operations being applied to sub-units which again consist of operations being applied to sub-sub-units, and so forth. Within a compiler this structure is usually represented as a so-called AST (abstract syntax tree).
If we print out an AST in parenthesized polish notation we would essentially end up with Lisp's syntax. This very elegant idea has a couple of advantages - it is extremely simple, easy to parse and totally generic (note though that the oft-heralded homoiconicity of Lisps is a red herring in my opinion - in every language that I know of it would not be difficult to represent a program's AST in the language itself).
On the other hand - at least for me - this genericity makes programs more difficult to read, especially at a glance, since it lacks redundancy. In Lisp the only carriers of information about the structure of a program are the names of operations and the nesting structure. In most main-stream languages however syntax is used as an additional redundant channel of communication. This redundancy makes it much easier to quickly grasp the structure of a piece of source code.
Have a look at this bit of C for example:
We can see that the same basic functionality is provided by very different syntactical elements depending on the context. The separation of terms for example is done by whitespace (top-level), ',' (declarations) and ';' (statements). Grouping is done by '()' (arithmetics, actually not shown in this example), operator precedence (arithmetics) and '{}' (statements). The application of an operation to arguments is expressed either in infix notation (arithmetics), prefix with '()' (function call), plain prefix (flow control keywords) or implicitly (declarations).
Of course this mess is far removed from the theoretical purity of Lisp's S-expressions. However it allows us to very quickly distinguish between different kinds of operations and different kinds of lists of terms. Looking for a declaration - spot names separated by whitespace, looking for function calls - find name + '()', and so on.
Redundancy therefore clearly serves to support readability (or "glanceability"). Too much of it on the other hand will certainly have an opposite effect. The optimal syntax will consequently add just enough redundancy to improve readability. (side note: There is also useless redundancy - Pascal is a lot more redundant than C, however mostly due to the fact that it uses keywords instead of punctuation and longer keywords. In my opinion this reduces readability. A similar argument could be made for Java.)
To maximize the effect of syntax it is also important that there is as little ambiguity in the correspondence between syntactic elements and semantic structure as possible. A nice counter-example is provided by C++. By "overloading" old syntax it becomes a lot harder to read (quickly) than C.
In Eta I wanted the overall look to stay somewhere in the vicinity of a traditional curly-brace language. At the same time I wanted it to be as simple and regular as possible while defining an unambiguous relationship between syntactic elements and semantics. (side note: This sounds a lot more goal-oriented than it was. Actually it took me quite a while to find out that these were the goals I was aiming for.)
This post is already long enough however, therefore I will postpone the details of Eta's syntax to the next post. As a small teaser the example from above rewritten in Eta:
Well, this topic has been discussed thoroughly, and I will only add to it to the extent that I am going to justify my own design decisions on the matter.
On the most abstract level a program can be thought of as a nested structure of operations being applied to sub-units which again consist of operations being applied to sub-sub-units, and so forth. Within a compiler this structure is usually represented as a so-called AST (abstract syntax tree).
If we print out an AST in parenthesized polish notation we would essentially end up with Lisp's syntax. This very elegant idea has a couple of advantages - it is extremely simple, easy to parse and totally generic (note though that the oft-heralded homoiconicity of Lisps is a red herring in my opinion - in every language that I know of it would not be difficult to represent a program's AST in the language itself).
On the other hand - at least for me - this genericity makes programs more difficult to read, especially at a glance, since it lacks redundancy. In Lisp the only carriers of information about the structure of a program are the names of operations and the nesting structure. In most main-stream languages however syntax is used as an additional redundant channel of communication. This redundancy makes it much easier to quickly grasp the structure of a piece of source code.
Have a look at this bit of C for example:
3 struct Point
4 {
5 float x, y;
6 };
7
8 float point_dist(Point p1, Point p2)
9 {
10 float dx = p2.x-p1.x, dy = p2.y-p1.y;
11
12 return sqrt(dx*dx + dy*dy);
13 }
We can see that the same basic functionality is provided by very different syntactical elements depending on the context. The separation of terms for example is done by whitespace (top-level), ',' (declarations) and ';' (statements). Grouping is done by '()' (arithmetics, actually not shown in this example), operator precedence (arithmetics) and '{}' (statements). The application of an operation to arguments is expressed either in infix notation (arithmetics), prefix with '()' (function call), plain prefix (flow control keywords) or implicitly (declarations).
Of course this mess is far removed from the theoretical purity of Lisp's S-expressions. However it allows us to very quickly distinguish between different kinds of operations and different kinds of lists of terms. Looking for a declaration - spot names separated by whitespace, looking for function calls - find name + '()', and so on.
Redundancy therefore clearly serves to support readability (or "glanceability"). Too much of it on the other hand will certainly have an opposite effect. The optimal syntax will consequently add just enough redundancy to improve readability. (side note: There is also useless redundancy - Pascal is a lot more redundant than C, however mostly due to the fact that it uses keywords instead of punctuation and longer keywords. In my opinion this reduces readability. A similar argument could be made for Java.)
To maximize the effect of syntax it is also important that there is as little ambiguity in the correspondence between syntactic elements and semantic structure as possible. A nice counter-example is provided by C++. By "overloading" old syntax it becomes a lot harder to read (quickly) than C.
In Eta I wanted the overall look to stay somewhere in the vicinity of a traditional curly-brace language. At the same time I wanted it to be as simple and regular as possible while defining an unambiguous relationship between syntactic elements and semantics. (side note: This sounds a lot more goal-oriented than it was. Actually it took me quite a while to find out that these were the goals I was aiming for.)
This post is already long enough however, therefore I will postpone the details of Eta's syntax to the next post. As a small teaser the example from above rewritten in Eta:
1 Point @ type : (x @ float, y @ float)
2
3 point_dist(p1 @ Point, p2 @ Point) @ float :
4 {
5 dx @ float : p2.x-p1.x
6 dy @ float : p2.y-p1.y
7
8 <- sqrt` dx*dx + dy*dy
9 }
Thursday, 22 July 2010
Eta
In the last two weeks what began as a small attempt at writing a work-saving template system for individual-based simulations turned into my first sort-of somewhat working (but not so work-saving) compiler for Eta (or η).
Eta is a project I have been working on/thinking about since a couple of years already. It started off with my increasing dislike for all the syntactic and semantic warts of the bloated mess that is C++. At the time I was desperately looking for an alternative, however everything I found that promised enough performance was either immature or only slightly less warty and bloated (sidenote: I think D is a much better language than C++ and I really hope it catches on. Still, it's far from being a good language IMHO.).
So I did what everybody who should instead really, really work on his PhD thesis (and I'm *not* talking about a PhD in Computer Science) would do - I started thinking about how to design my own language.
As others have said language design tends to be more successful when it tries to scratch a personal itch than when it attempts to solve other people's problems. In my case I really wanted a language that would make it easier and more fun to implement the simulations I am working on. That means the language had to be
macros"string mixins", traits and conditional compilation?).
Apart from these general principles I had a couple of specific technical ideas about mechanisms I wanted to include in the language. I will leave the details on that, on how I implemented Eta and on how the language looks like currently to the next post, however.
Eta is a project I have been working on/thinking about since a couple of years already. It started off with my increasing dislike for all the syntactic and semantic warts of the bloated mess that is C++. At the time I was desperately looking for an alternative, however everything I found that promised enough performance was either immature or only slightly less warty and bloated (sidenote: I think D is a much better language than C++ and I really hope it catches on. Still, it's far from being a good language IMHO.).
So I did what everybody who should instead really, really work on his PhD thesis (and I'm *not* talking about a PhD in Computer Science) would do - I started thinking about how to design my own language.
As others have said language design tends to be more successful when it tries to scratch a personal itch than when it attempts to solve other people's problems. In my case I really wanted a language that would make it easier and more fun to implement the simulations I am working on. That means the language had to be
- fast. It *does* make a difference whether I have to wait 5 days or 8 days for a set of simulations to finish.
- strictly and statically typed. As I said before the major difficulty when writing simulations is that errors are often silent. Every bit of static guarantee the compiler can give helps.
- interoperable with C/C++. I'm not going to reimplement all the libraries I use.
- expressive. For reasons of efficiency dynamic operations are often out in simulations. Therefore similar redundant patterns start to show up at lots of different places. E.g. if I add a new trait to an individual it has to be read, set, initialized, mutated, written to the data file, read from a config file, etc. Some of it can be alleviated by some advanced template wizardry but in the end I sooner or later usually fall back to external code generation. My ideal language would have built-in compile-time macros to solve this problem.
- clear and unambiguous. One problem with C++ is that understanding what *exactly* a particular piece of code does can be non-trivial. Apart from syntactic idiosyncrasies things like silent shadowing of globals and implicit conversion rules make it necessary to be aware of a big amount of context to understand local semantics. This is especially a problem for simulations since (due to lack of external tests) code review plays an essential role in ensuring their correctness. A good language should therefore reduce the amount of context necessary to understand a piece of code as much as possible.
Apart from these general principles I had a couple of specific technical ideas about mechanisms I wanted to include in the language. I will leave the details on that, on how I implemented Eta and on how the language looks like currently to the next post, however.
Tuesday, 4 May 2010
C++ sucks for simulations
Today I once again had to realize that C/C++ is really a bad language to use for numerical simulations. In two heavily scrutinized (and often used) pieces of code I found two simple, yet far-reaching bugs which would immediately have been spotted by the compiler or the run-time system, respectively, had I used a decent language.
I implemented the first version of the simulation I am currently (again) working on about six years ago. Since then I made countless changes and refinements, although the general structure remained the same (yay structured programming). In particular the core functions of the model class (which implement what the model actually does) I tend to revise quite often in an iterative process of checking results, trying to understand them and coming up with new ways to test whether what I come up with is what's actually happening.
The stable and general part of the code I am producing I usually pull into a private library (which is available from sourceforge but so much a work in progress that I won't link to it) after some time.
One of the bugs occurred in the core model, the other one in the library, both in pieces of code I have checked and re-checked dozens of times. Both bugs were really, really stupid.
I will keep the story of how I actually found these bugs in the end for another blog post, suffice it to say that one of them is a Heisenbug, i.e. it had no effect in the debug version of the program.
Bug 1:
Without going into too much detail, the basic idea behind this code is that two individuals a and b have to negotiate their "role" (roleA and roleB) in the conflict. How this negotiation happens depends on the mode of role assignment each individual prefers (sym_a and sym_b). In one particular mode (sym_random) b automatically has the opposite role of a. Therefore in line 7 it should be roleA instead of sym_a.
I know, it's just a typo and a stupid one at that (although the code shown here is slimmed down quite a bit, in the original version the mistake is a bit more difficult to spot). Still, one reason it can go unnoticed is that C++ happily let's me convert between enum, int and bool without as much as a cringe (BTW, the fact that I have to use int for sym_a and sym_b instead of an enum is due to another quirk of C++ - enums don't play well with IO).
Bug 2:
Ok, this one is slightly embarassing. In a misguided attempt at optimisation I decided to rely on the input being valid (i.e. between 0 and 1). Which was sort of fine since in the original version I had two debug asserts checking for validity. Unfortunately it turned out that if I switched on SSE* code generation in gcc values of p==1.0 (which passed the asserts) lead to silent overflows in the multiplication so that the function always returned false. After I finally found it the bug was easily fixed by bypassing the comparison for values <=0 and >=1.
These two examples demonstrate two problems of C++ which make numerical programming significantly more error prone than necessary. Implicit conversion and silent arithmetic errors are by far not the most common sources of bugs in my programs. However if they occur the resulting bugs are among the hardest to find.
Unfortunately all languages that would offer enough static and dynamic checking to avoid these types of bugs come with a strong efficiency penalty (and, yes, 50% longer runtime *is* too much). It seems for now we will just have to make do with a bad language.
I implemented the first version of the simulation I am currently (again) working on about six years ago. Since then I made countless changes and refinements, although the general structure remained the same (yay structured programming). In particular the core functions of the model class (which implement what the model actually does) I tend to revise quite often in an iterative process of checking results, trying to understand them and coming up with new ways to test whether what I come up with is what's actually happening.
The stable and general part of the code I am producing I usually pull into a private library (which is available from sourceforge but so much a work in progress that I won't link to it) after some time.
One of the bugs occurred in the core model, the other one in the library, both in pieces of code I have checked and re-checked dozens of times. Both bugs were really, really stupid.
I will keep the story of how I actually found these bugs in the end for another blog post, suffice it to say that one of them is a Heisenbug, i.e. it had no effect in the debug version of the program.
Bug 1:
1 const int sym_a = a.symmetry();
2 const int sym_b = b.symmetry();
3 enum {sym_random = 0, sym_noRoles = 1};
4
5 const bool roleA = sym_a == sym_noRoles ? true : rng(2);
6 const bool roleB = sym_b == sym_noRoles ? true :
7 (sym_a == sym_random ? !sym_a : rng(2));Without going into too much detail, the basic idea behind this code is that two individuals a and b have to negotiate their "role" (roleA and roleB) in the conflict. How this negotiation happens depends on the mode of role assignment each individual prefers (sym_a and sym_b). In one particular mode (sym_random) b automatically has the opposite role of a. Therefore in line 7 it should be roleA instead of sym_a.
I know, it's just a typo and a stupid one at that (although the code shown here is slimmed down quite a bit, in the original version the mistake is a bit more difficult to spot). Still, one reason it can go unnoticed is that C++ happily let's me convert between enum, int and bool without as much as a cringe (BTW, the fact that I have to use int for sym_a and sym_b instead of an enum is due to another quirk of C++ - enums don't play well with IO).
Bug 2:
1 /** Gives true with a probability of p. */
2 bool choice(float p)
3 {
4 return (*this)() < Type((this->getMax()) * p);
5 }
Ok, this one is slightly embarassing. In a misguided attempt at optimisation I decided to rely on the input being valid (i.e. between 0 and 1). Which was sort of fine since in the original version I had two debug asserts checking for validity. Unfortunately it turned out that if I switched on SSE* code generation in gcc values of p==1.0 (which passed the asserts) lead to silent overflows in the multiplication so that the function always returned false. After I finally found it the bug was easily fixed by bypassing the comparison for values <=0 and >=1.
These two examples demonstrate two problems of C++ which make numerical programming significantly more error prone than necessary. Implicit conversion and silent arithmetic errors are by far not the most common sources of bugs in my programs. However if they occur the resulting bugs are among the hardest to find.
Unfortunately all languages that would offer enough static and dynamic checking to avoid these types of bugs come with a strong efficiency penalty (and, yes, 50% longer runtime *is* too much). It seems for now we will just have to make do with a bad language.
Sunday, 15 November 2009
Android, Java, Eclipse
During the weekend I wrote my first little android application (Leander, a frontend for dict.leo.org). I did it mainly to earn myself an Archos 5, but beyond that I was also just curious. This was the first time I wrote something for a different platform than general purpose PC/workstation/server, it was the first time I really used Eclipse and it was the first time in at least ten years that I used Java again.
As is to be expected from a resource-limited device the SDK feels a bit constrained and decidedly non-fancy. On the other hand that makes it relatively clear and easy to pick up.
I did not like Java when I first had to use it in a graphics programming course back in the nineties and I still do not really care for it. Its verbosity and redundancy annoy me. I think Java just feels overwhelmingly pedestrian.
All the rough edges and annoyances the whole experience could have had however were muffled by Eclipses constant supervision. I do not know whether it is the progress of technology or the different attitude of the language but the last time I used an IDE (Visual C++ back in the nineties as well), I did not feel nearly as cared for as this time.
All this hand-holding makes development sort of a brainless activity - just follow the suggestions by the IDE, copy and paste a few things from the docs or online sources... done. Anyways it is fun and I am curious to see whether my app is actually going to be used by anyone ;-).
As is to be expected from a resource-limited device the SDK feels a bit constrained and decidedly non-fancy. On the other hand that makes it relatively clear and easy to pick up.
I did not like Java when I first had to use it in a graphics programming course back in the nineties and I still do not really care for it. Its verbosity and redundancy annoy me. I think Java just feels overwhelmingly pedestrian.
All the rough edges and annoyances the whole experience could have had however were muffled by Eclipses constant supervision. I do not know whether it is the progress of technology or the different attitude of the language but the last time I used an IDE (Visual C++ back in the nineties as well), I did not feel nearly as cared for as this time.
All this hand-holding makes development sort of a brainless activity - just follow the suggestions by the IDE, copy and paste a few things from the docs or online sources... done. Anyways it is fun and I am curious to see whether my app is actually going to be used by anyone ;-).
Google Go, OOP, Interfaces and Inheritance
It seems these days new programming languages and in particular "systems" programming languages aiming to replace C/C++ are sprouting like mushrooms. I like programming languages therefore for me this is fun, the general public however usually is utterly unaware of these small languages, at least until they become old and somewhat established.
Last week however a new language was presented by nobody different than Google itself. Accordingly it made quite a splash. After everybody had cooled down a bit it turned out that this new language - Go - was mostly quite unremarkable. It consists mainly of a non-daring combination of tried and true language features each of which has been around for quite a while, put together with a strong focus on the simple and non-fancy.
It seems the only feature that received a bit of lasting attention is the lack of classical OOP. Although dissing OOP is sort of a trend at the moment, presenting a new supposedly mainstream language without classes and inheritance still attracts attention.
Instead of inheritance Go promotes composition. Polymorphism is achieved by a very simple mechanism - instead of letting classes declare the conformance to an interface at the time of the declaration of a class, every type that has the right combination of methods associated with it automatically conforms to an interface.
This last feature is it that the designers of Go (and quite a few other people) seem to be most excited about.
The funny thing is - this has been one of my pet peeves for ages and is actually quite an old hat. When I started to learn C++ (coming from Objective C) one of the things that bugged me most was that which interface a class implemented (or abstract base class it derived from in C++ terminology) was practically part of its implementation details. The feeling that this was a bad idea became even stronger when I later on learned Java. There is all this nice polymorphism and reflection but if you just quickly want to make a new interface for an existing class you have to jump through all sorts of hoops.
Then I found out that gcc had this nice C++ extension called 'signatures' which worked more or less exactly like Go's interfaces. At the time when I discovered them the documentation still bravely stated that signatures were being considered as an official part of the language. As we all know, this never happened so I didn't use them (as far as I know nobody did) and instead toyed with the idea of autogenerating some kind of template-based interface-adaptor.
It turned out that I wasn't the first to do so. A language called Heron was based on the same idea and even implemented as a front end to C++. It seems the author however lost interest at some point and abandoned the language.
Further there is the greatly underappreciated and sadly abandoned language Sather which separated inheritance and polymorphism as well.
Obviously not a new idea, thus, but unfortunately one that never took. A while ago I even proposed the same feature for the D language on their mailing list - of course unsuccesfully, the feature was too alien, my explanation too bad and my reputation too non-existent.
At some point my frustration with the rigidity of OOP (and many, many other things) in C++ became so big that I did what everybody seems to do - I started working on my own programming language. Separation of code reuse and polymorphism are one of its key features (the scope of the project long since snowballed from a modest redesign of C++ to a complete start from scratch, but that's a different story).
Accordingly it gives me a bit of a stale feeling to see everybody getting all excited about this supposedly revolutionary and brilliant feature. On the other hand having the momentum of Google behind it will hopefully finally give the idea enough exposure to find out whether it is indeed a viable alternative to classical OOP.
Last week however a new language was presented by nobody different than Google itself. Accordingly it made quite a splash. After everybody had cooled down a bit it turned out that this new language - Go - was mostly quite unremarkable. It consists mainly of a non-daring combination of tried and true language features each of which has been around for quite a while, put together with a strong focus on the simple and non-fancy.
It seems the only feature that received a bit of lasting attention is the lack of classical OOP. Although dissing OOP is sort of a trend at the moment, presenting a new supposedly mainstream language without classes and inheritance still attracts attention.
Instead of inheritance Go promotes composition. Polymorphism is achieved by a very simple mechanism - instead of letting classes declare the conformance to an interface at the time of the declaration of a class, every type that has the right combination of methods associated with it automatically conforms to an interface.
This last feature is it that the designers of Go (and quite a few other people) seem to be most excited about.
The funny thing is - this has been one of my pet peeves for ages and is actually quite an old hat. When I started to learn C++ (coming from Objective C) one of the things that bugged me most was that which interface a class implemented (or abstract base class it derived from in C++ terminology) was practically part of its implementation details. The feeling that this was a bad idea became even stronger when I later on learned Java. There is all this nice polymorphism and reflection but if you just quickly want to make a new interface for an existing class you have to jump through all sorts of hoops.
Then I found out that gcc had this nice C++ extension called 'signatures' which worked more or less exactly like Go's interfaces. At the time when I discovered them the documentation still bravely stated that signatures were being considered as an official part of the language. As we all know, this never happened so I didn't use them (as far as I know nobody did) and instead toyed with the idea of autogenerating some kind of template-based interface-adaptor.
It turned out that I wasn't the first to do so. A language called Heron was based on the same idea and even implemented as a front end to C++. It seems the author however lost interest at some point and abandoned the language.
Further there is the greatly underappreciated and sadly abandoned language Sather which separated inheritance and polymorphism as well.
Obviously not a new idea, thus, but unfortunately one that never took. A while ago I even proposed the same feature for the D language on their mailing list - of course unsuccesfully, the feature was too alien, my explanation too bad and my reputation too non-existent.
At some point my frustration with the rigidity of OOP (and many, many other things) in C++ became so big that I did what everybody seems to do - I started working on my own programming language. Separation of code reuse and polymorphism are one of its key features (the scope of the project long since snowballed from a modest redesign of C++ to a complete start from scratch, but that's a different story).
Accordingly it gives me a bit of a stale feeling to see everybody getting all excited about this supposedly revolutionary and brilliant feature. On the other hand having the momentum of Google behind it will hopefully finally give the idea enough exposure to find out whether it is indeed a viable alternative to classical OOP.
Labels:
google go,
inheritance,
interfaces,
OOP,
programming
Monday, 26 October 2009
Scientific versus "regular" programming - part I
A huge part of the effort (and the resulting progress) in computer science is dedicated to making it easier for people to create better programs in a shorter amount of time. To this end new tools and methodologies are developed.
However if we zoom in a bit differences between different areas of application of programming become obvious. Consequently the demands placed on the required tools and methods differ as well.
In my field (theoretical biology) and I think generally in areas of science that require the development of simulation software programming happens under very special conditions that lead to a unique set of requirements for the process of software creation.
what is a good program?
Many clever people have written whole books on the topic and I am certainly not an expert, but in a nutshell a good program in most situations has to fulfill these criteria:
However if we zoom in a bit differences between different areas of application of programming become obvious. Consequently the demands placed on the required tools and methods differ as well.
In my field (theoretical biology) and I think generally in areas of science that require the development of simulation software programming happens under very special conditions that lead to a unique set of requirements for the process of software creation.
what is a good program?
Many clever people have written whole books on the topic and I am certainly not an expert, but in a nutshell a good program in most situations has to fulfill these criteria:
- correctness - It has to do the things it is supposed to do (and only those).
- efficiency - It has to do them using a reasonable amount of resources (time, memory, etc.).
- maintainability - It has to be reasonably easy to change the program in the future.
Making it easier for people to make programs conform to these criteria (or at least find out whether they do) with a reasonable amount of effort has been the main driving force behind the development of new languages, platforms, IDEs, coding conventions, etc. Accordingly it is nowadays a *lot* easier to produce correct, efficient and maintainable code than it was, say thirty years ago.
Although similar the criteria for what makes a "good" program in a scientific context differ in important details.
efficiency
It has often been said (in many variations) that Moore's law made efficiency unimportant. This is certainly true in many areas as shown by the success of dynamic, interpreted (and horribly slow) languages such as Ruby or Python.
For someone who writes and uses simulation programs however time is always a limiting factor (disk space and memory are others but less so in recent years). Given more time (or higher execution speed) it is possible to test more parameter combinations, build in more details, run more replicates or observe more long term dynamics - all of which (might) lead to better results, which make for better publications which will bring more fame, fortune and general happiness.
execution speed is important
maintainability
The need to program in a way that makes it easy (or at least possible) to make changes to a program later on has lead to the evolution of whole industries.
In the scientific context this is only an issue for library code and tools. Most of the code written by the scientist herself is usually a one-off effort and stored in the virtual attic after publication of the corresponding paper(s).
There is a related issue of understandability and clarity of code but I will talk about this later.
maintainability is (with certain caveats) a minor problem
correctness
I think program correctness is maybe the aspect where scientific programming differs most from "mainstream" programming.
The correctness of an operating system or a game is determined by how much the respective program behaves according to specifications. Bugs are found by people running into situations where the program behaves in a way it shouldn't (crashes, rendering glitches, hangups, etc.). Observing the program is therefore ultimately the way most bugs are detected in such a situation. Luckily this also means that those bugs that have the strongest effect on program behavior tend to be the easiest to detect.
In a simulation on the other hand the behavior is the outcome of the program. The program is correct if the behavior is produced according to the specified rules. Of course simulations also have easily observable bugs (e.g. the program crashes) but these are not dangerous. Many errors however "just" lead to wrong results. These bugs are very dangerous because they can go entirely undetected while making the whole program (or at least the work done with it) effectively worthless. Especially in more complicated simulations in principle the only way to find these bugs is by rigorous examination of the source code.
correctness is essential, difficult to obtain and even more difficult to prove
clarity
This leads us directly to an additional criterion for program quality that is usually seen as a part of maintainability but in the context of scientific programming deserves in my opinion a bullet point on its own - clarity and legibility of the source code.
If some (serious) bugs can only be found by reasoning about the source code then it becomes of paramount importance to write the code in a way that makes it easy to reason about. In this sense clarity is a means to fulfill the correctness criterion.
In a scientific context however the source code of a program is more than just the intermediate stage towards producing an executable that can be used. An essential part of the way science happens is that one scientist's results have to be reproducible by other scientists. In the empirical fields that means that methods are published down to the last onerous detail. In a mathematical paper enough steps of a calculation are given that it is possible to retrace the authors' steps (for a suitable definition of 'possible'...). Given the notorious dissociation between source code and documentation the code ultimately is the authoritative source on what a simulation does (unfortunately there is no real standard for the publication of source code (yet), although usually most authors at least offer to provide the source code on request - but that is a different blog post). Source code therefore is also a means of communication between scientists and therefore should be written in a way that makes it as easy to understand as possible.
In my opinion this is a vastly underappreciated aspect of at least those programming courses for scientists that I am aware of.
clarity of source code is essential
It should be clear by now that producing a good program requires a specific approach in a scientific context. In the next part of this post I will explain which consequences the specific "socio-economic environment" has for scientific programming. Then I will explore the consequences for the design of better tools for scientific programming.
update (27/10/09 10:28)
Please also check out the interesting comments on reddit.
Although similar the criteria for what makes a "good" program in a scientific context differ in important details.
efficiency
It has often been said (in many variations) that Moore's law made efficiency unimportant. This is certainly true in many areas as shown by the success of dynamic, interpreted (and horribly slow) languages such as Ruby or Python.
For someone who writes and uses simulation programs however time is always a limiting factor (disk space and memory are others but less so in recent years). Given more time (or higher execution speed) it is possible to test more parameter combinations, build in more details, run more replicates or observe more long term dynamics - all of which (might) lead to better results, which make for better publications which will bring more fame, fortune and general happiness.
execution speed is important
maintainability
The need to program in a way that makes it easy (or at least possible) to make changes to a program later on has lead to the evolution of whole industries.
In the scientific context this is only an issue for library code and tools. Most of the code written by the scientist herself is usually a one-off effort and stored in the virtual attic after publication of the corresponding paper(s).
There is a related issue of understandability and clarity of code but I will talk about this later.
maintainability is (with certain caveats) a minor problem
correctness
I think program correctness is maybe the aspect where scientific programming differs most from "mainstream" programming.
The correctness of an operating system or a game is determined by how much the respective program behaves according to specifications. Bugs are found by people running into situations where the program behaves in a way it shouldn't (crashes, rendering glitches, hangups, etc.). Observing the program is therefore ultimately the way most bugs are detected in such a situation. Luckily this also means that those bugs that have the strongest effect on program behavior tend to be the easiest to detect.
In a simulation on the other hand the behavior is the outcome of the program. The program is correct if the behavior is produced according to the specified rules. Of course simulations also have easily observable bugs (e.g. the program crashes) but these are not dangerous. Many errors however "just" lead to wrong results. These bugs are very dangerous because they can go entirely undetected while making the whole program (or at least the work done with it) effectively worthless. Especially in more complicated simulations in principle the only way to find these bugs is by rigorous examination of the source code.
correctness is essential, difficult to obtain and even more difficult to prove
clarity
This leads us directly to an additional criterion for program quality that is usually seen as a part of maintainability but in the context of scientific programming deserves in my opinion a bullet point on its own - clarity and legibility of the source code.
If some (serious) bugs can only be found by reasoning about the source code then it becomes of paramount importance to write the code in a way that makes it easy to reason about. In this sense clarity is a means to fulfill the correctness criterion.
In a scientific context however the source code of a program is more than just the intermediate stage towards producing an executable that can be used. An essential part of the way science happens is that one scientist's results have to be reproducible by other scientists. In the empirical fields that means that methods are published down to the last onerous detail. In a mathematical paper enough steps of a calculation are given that it is possible to retrace the authors' steps (for a suitable definition of 'possible'...). Given the notorious dissociation between source code and documentation the code ultimately is the authoritative source on what a simulation does (unfortunately there is no real standard for the publication of source code (yet), although usually most authors at least offer to provide the source code on request - but that is a different blog post). Source code therefore is also a means of communication between scientists and therefore should be written in a way that makes it as easy to understand as possible.
In my opinion this is a vastly underappreciated aspect of at least those programming courses for scientists that I am aware of.
clarity of source code is essential
It should be clear by now that producing a good program requires a specific approach in a scientific context. In the next part of this post I will explain which consequences the specific "socio-economic environment" has for scientific programming. Then I will explore the consequences for the design of better tools for scientific programming.
update (27/10/09 10:28)
Please also check out the interesting comments on reddit.
Subscribe to:
Posts (Atom)