Wednesday, 9 April 2014

UI grievances in modern OSes (Death to mouse-over highlights!)

TL;DR: Had to replace aging Windows XP. Tried Windows 8 and found that it sucked (stupid Aero mouse-over highlights). Tried Linux and found that it still sucked (lack of UI polish). Went back to the idea of using Windows 7, and set it to the classic theme. Now content, but fearful for the future.


Classic theme, not even the XP look, but the older Windows 98 look!



So, recently I bought a new laptop, which I use mainly for work - software development. It replaces my 5-year old laptop, still running Windows XP. Time was very much running out on it: Microsoft is stopping support for XP in April this year. That meant, it was time to choose a new OS.

I'm a Windows guy, so I was wondering on a choice between Windows 7 and Windows 8. Windows 8 is said to have better performance and better driver support (my own anecdotal experience later on seemed to support that), yet it also has the Metro interface, woefully inadequate for a desktop - and even more so for power users. However, with utilities such as Classic Shell, Metro was something that could be easily disabled (remove the Metro home screen and restore the Start menu),  so I wasn't too worried about it. Overall, I was keen to try out Windows 8.

So I did... but things didn't work out very well. Like I mentioned, the OS seemed leaner, faster, and some drivers worked better out of the box. But crucially, Windows 8 does not have true Classic UI themes/mode, it only supports the Aero theme engine. And personally I really hate Aero themes (even the Metro version). Specifically, I hate all those stupid mouse-over highlights that happen all over the interface. By "mouse-over highlights", I'm referring here to any graphical UI change that happens just because of moving the mouse cursor around.


Objectively speaking, these effects are pure eye-candy: they offer a visual aesthetic of sorts, an artistic effect that might be pleasant to some. But they do nothing to improve the functionality of the UI. Rather, it denigrates it, because it introduces a distraction, an unnecessary cognitive load on the user. It may have appeal to users who prefer said artistry, but for those who prefer clean functionality (like myself), it is unwelcome. That said, there are some minor mouse-over highlights I can live with, if the effect is subtle. Even the Windows XP interface (Luna) had a few of these already. But the ones in the Aero interface are not subtle at all, and in particular there is one which is an absolute deal-breaker: the list selection highlight:


This highlighting features prominently in the Windows File Explorer, as well as many other applications (Eclipse for example) that use list views, tree views, etc.. This effect is annoying not just because of the distraction during the time you are moving the cursor, but even afterwards, given that the highlight color is very similar to the selection color! This makes it hard to understand at a quick glance which are the selection items and which are not. Ridiculous! :/

This can't be disabled in the Windows UI, but a custom Windows theme might be able to disable it. I searched plenty for a theme that would do that, but found none. Even the many themes I found that purpose to imitate the XP interface, aka Luna (my favorite window theme so far), failed to address that. Not to mention they often also had visual bugs, or were unpolished in certain visual aspects. Examples: this, this, and this.

No joy. My second hope was a Windows customization program: WindowBlinds from Stardock software. I gave it a try, and tried to look for themes that imitated the Luna interface, or at least, otherwise clean themes with few mouse-over highlights and other unnecessary effects. I couldn't find any like such. I then turned to trying to customize the WindowBlinds theme myself. I had to purchase the full version of WindowBlinds, so I could use SkinStudio, the skin/theme editing software. The software has a very, very extensive collection of customization points... but crucially, there was a blind spot for the customization I wanted!... Apparently it couldn't not change those mouse-over highlights, at least not in a proper way (this thread in the Stardock forums has more info).

No joy... There was still the option of trying to edit Windows 8 themes myself (using resource file hacking and whatnot), but at this point that was mounting to be an huge task. I have better things to do. But still, I was getting desperate. At this point, I even considered using Linux. It had been more than a decade (!) since I last tried using a Linux as a main OS. I would never consider it for use in a desktop/leisure machine due to all the programs (mostly games) that I want to run, but maybe for a work machine, it would be feasible. Of recent distros, I was only familiar with Ubuntu - I had a VM with Ubuntu for testing out programs in Linux. But UI wise Ubuntu was going the way of Mac OS (blergh - too much eye candy) and/or of a tablet paradigm (I'm not even sure which). No go.

Linux Mint looked promising though. I installed it and gave it a go... but still, no good. Things have definitely improved with Linux with regards to hardware setup and configuration, everything worked fine, devices auto-detected, plug-and-play, all good. It was nice to see that. But the UI was still lacking, there was still a minor but clear lack of polish. Just as an example, when I opened a menu, there would be a brief fraction of a second (almost indiscernible) where a black rectangle would be drawn in the whole area where the menu would appear, just before the actual menu would be drawn - it was a sort of flicker. So, even though I managed to find some clean themes (even a Window XP lookalike theme, including icons!), otherwise the lack of polish, some visual bugs/artifacts, or certain limited functionality in say, the file Explorer, kept me from embracing Linux usage (I'm not going into detail why, since that would be material for a whole new post). 
It was a bit disappointing, but not unexpected at all, that even after a decade, the Linux crowd still failed to get their act together, at least to the desktop environment.

So in the end I just got fed up, wiped my disk to install Windows 7, and set it up to use the classic theme (which is the Windows 98 look). I was quite glad to go back developing and not messing around with all this UI grief that I got from everywhere. My heart was not fully at ease though: while I was quite happy with the Windows 7 UI, per se, I feared for the future. I knew I would be fine for some years to come, but after that, when my hardware gets too old, when Windows 7 gets too old in terms of support and I need a new OS, where will I turn?

Saturday, 17 August 2013

The state and future of Eclipse, as an IDE and a platform.

Doug Schaefer from the Eclipse CDT team just brought up a very interesting post:
This tied in with a lot of things I was thinking recently about the state and future of Eclipse, as an IDE and as a platform. I hadn't ever brought it up before, so I took an opportunity and made a huge comment on Doug's blog about it (I actually had to split it in three posts due to size limitations). I do hope people read it, especially those influential in the Eclipse development sphere.

I wouldn't repost it here, you can read it at Doug's blog:
http://cdtdoug.blogspot.co.uk/2013/07/eclipse-smells-kind-of-dead.html?showComment=1376758343854#c491889590913528059

TL;DR: In summary, I talk a bit about:
  • How it seems like the technical quality of Eclipse (the platform, and the JDT IDE) has not only failed to evolve but even declined a bit, more or less since the inception of the 4.x series.
  • It also has not evolved much in terms of framework support for IDEs of other languages based on Eclipse (apart from the promising, but not yet very mature, DLTK project).
  • Other IDEs are catching up (IntelliJ IDEA, MonoDevelop, even Netbeans), and becoming strong competitors.

Tuesday, 15 May 2012

Hazards and pitfalls of equals() on class hierarchies

The problem

I've recently got bitten by some very nasty equals() hazards when extending classes. Here is a cautionary tale, that taught me the following: if you extend a class which implements its own equals() method, always carefully check the equals() method of that base class, and make sure to create a new equals() override in the subclass if that is required - and it will be, if you add new structural data to the subclass which affects the logical equality of instances of that class.
But even if your subclass doesn't change anything that affects the notion of logical equality, still remember to check the equals() method of the base class, to avoid any pitfalls. Merely extending a class may break the equals() contract! Recently I fell into one such big pitfall whilst working with Java on the DDT project. Let us look at the issue in detail. I had this code for the superclass:

class ScriptElementImageDescriptor
...
    public boolean equals(Object object) {       
        if (object == null || !ScriptElementImageDescriptor.class.equals(object.getClass())) {           
            return false;
        }
        ScriptElementImageDescriptor_Fix other= (ScriptElementImageDescriptor_Fix)object;
       
        return (fFlags == other.fFlags)
            && (fSize.equals(other.fSize)) && (fBaseImage.equals(other.fBaseImage));

     }


I created a somewhat trivial subclass of ScriptElementImageDescriptor, and the problem now was that instances of that subclass were no longer equal to any other instance whatsoever. This is due to the way that ScriptElementImageDescriptor.equals() does the check for the instance's runtime type:
    || !ScriptElementImageDescriptor.class.equals(object.getClass())
This check is not the same as the more common instanceof check. Rather, this check will fail unless the runtime type of object is exactly that of ScriptElementImageDescriptor. So subclasses will fail the check, just the same as unrelated classes, and that's what caused the bug.

And this bug was particularly dangerous because that descriptor class was not an actual domain model class for the application - it wasn't used in any direct user/application functionality, but rather it was related to performance internal functionality: it was mainly used as a descriptor for caches of image resource objects. Therefore, what this bug caused was a silent resource leak, one which was quite harder to trace than a direct application functionality bug (like a UI bug or crash). Only by sheer luck did I write some other unrelated code (code that was disposing and recreating a certain UI component 100 times more often than normal, to test flickering issues), which caused this bug to manifest clearly and early, very soon after the original change that introduced it. If this bug had shipped, it would have been very, very hard to replicate (it might take hours of normal application usage to hit the leak limits) and to diagnose (as even once the leak is hit, it wouldn't be clear at all what caused it!).


So the horridness of this bug ingrained in my mind that I should always check and remember how a superclass does equals() (and hashcode()). But my considerations didn't end with the ScriptElementImageDescriptor case - I started to think about this issue in a more general way, considering all sorts of cases. What should I do with my code (assuming I can also modify the superclass) when I create a subclass, that may or may not add structural data affecting its notion of equality (sometimes called an aspect) ?

The solutions

My first idea to address this issue was to change the superclass equals() to use the more common instanceof runtime type check, like this (I have now simplified the classes for the rest of the article):

class SuperClass {
    int fieldA;
    int fieldB;
   
    public boolean equals(Object object) {
        if (
!(object instanceof SuperClass))
            return false;

        SuperClass other = (SuperClass)object;
        return fieldA == other.fieldA && fieldB == other.fieldB;
    }
}


Now, if SubClass doesn't modify the notion of equals, everything is done. And if it does (say, a new field is added), then a Subclass.equals() override is created which does its own checks, but calls the superclass's equals() method to check the equality specifics relating to the superclass data/state:

class SubClass extends SuperClass {
    int subclassField;
    

    public boolean equals(Object object) {       
        if (!(object instanceof SubClass))

            return false;
       
        SubClass other = (SubClass)object;

        return subclassField == other.subclassField && super.equals(other);
    }


Hold on a second...

...is the above code correct? Well, not quite! That code was my first idea, but it was clear it had a problem: if it can be that SuperClass and SubClass objects end up being compared to each other, you will get different (and thus incorrect) results if the equals() method is called with a SuperClass receiver, with a SubClass parameter. So:
    SuperClass superClass = new SuperClass(...)
    SuperClass subClass = new SubClass(...)
   
superClass.equals(subClass)
may not return the same value as:
    subClass.equals(superClass)
because different equals() methods will be called. Returning a different value is a clear violation of the equals() symmetry contract in Java (and in other languages it's likely to also be a contract violation, or, at the very least, an ugly and brittle design). This problem is discussed in more detail in Items 7 and 8 of Joshua Bloch's Effective Java Programming Language Guide. He mentions that: "There is simply no way to extend an instantiable class and add an aspect while preserving the equals contract.", but I am not sure that is entirely correct. For example, a potential fix to this is to change equals() so that it requires that the runtime type of the objects being compared be exactly the same. That would be the second solution:



class SuperClass {
    int fieldA;
    int fieldB;
   
    public boolean equals(Object object) {

        if (this == object)
            return true;
        if (object == null || this.getClass() != object.getClass())
            return false;
       
        return equalsOther((SuperClass)object);
    }
   
    public boolean equalsOther(SuperClass other) {
        return fieldA == other.fieldA && fieldB == other.fieldB;
    }
}


class SubClass extends SuperClass {
    int subclassField;

    public boolean
equalsOther(SuperClass object) {       
        SubClass other = (SubClass)object;
        return subclassField == other.subclassField && super.equalsOther(other);
    }
}


This is now a correct implementation of equals(), useful without violating its contract. Also added is the if(this == object) check just as a minor optimization. But this solution still has a significant disadvantage. Because we reverted to the use of a direct class equality requirement:
    this.getClass() != object.getClass()
then other subclasses that *don't* change the notion of logical equality of their parent will not be equal to instances of their superclass, even though they should be logically equal (for those familiar with the concept, we forcibly cause *all* subclass instances to break the Liskov substitution principle). Whether this limitation is an actual problem or not, that depends on your code, and what you want to do with your subclassses, if you have any at all. This approach can be a significant problem more often than you think at first, though. For example, even if in your code there are no subclasses of such a base class, if you use ORM persistence frameworks like Hibernate, these use reflection and byte-code generation to dynamically create proxy subclasses of the classes you are trying to persist! So the above pattern totally breaks if you are using such frameworks, making it unequivocally unusable.

The final solution

There is a way to address this issue without any drawbacks (well, other than verbose code). Basically the approach I figured out uses a combination of both instanceof and class comparison. The main type check will be instanceof as in the first solution. However, after the instanceof check, we will do another type check on the other object to prevent symmetry problems: we will ask the other object what is the minimum class (most derived class) that the object can be equal to. This is done by introducing a new method called getEqualityClassRequirement(). See the code:



class SuperClass {
    int fieldA;
    int fieldB;
   
    public boolean equals(Object object) {
        if (this == object)
            return true;  
        if (!(object instanceof SuperClass))           
            return false;
       
       
SuperClass other = (SuperClass) object;
        return other.getEqualityClassRequirement() == this.getEqualityClassRequirement() 
            && equalsOther(other);
    }
   
   
protected Class<?> getEqualityClassRequirement() {
        return
SuperClass.class;
    }
   
    public boolean equalsOther(SuperClass other) {
        return fieldA == other.fieldA && fieldB == other.fieldB;
    }
}


Then, as result, subclasses of SuperClass that modify the notion of logical equality must override getEqualityClassRequirement to specify their own class type as a requirement:


class SubClass extends SuperClass {
    int subclassField;
 
    
    protected Class<?> getEqualityClassRequirement() {
        return
SubClass.class;
    }
    
    public boolean equalsOther(SuperClass object) {       
        SubClass other = (SubClass)object;
        return subclassField == other.subclassField && super.equalsOther(other);
    }
}

This is the best way I came up to fully address this issue in the Java language. If you use Eclipse JDT, this could be a good opportunity to create a code template to produce the boilerplate above.
I can see that a lot of verbosity-averse programmers will be cringing at the above code. If you have such aversions, or if you are just feeling particularly lazy whilst coding a certain class, you can still use the first solution (the lightweight version which just uses instanceof), as long as you make sure your equals() method is final. This will turn the first solution into a correct one, although with severe limitations (no subclasses will be able to change the notion of logical equality). This is actually fine for code that is not released externally (i.e., as a library component), because if you ever run into the limitation, you can just change the equals() code to one of the other solutions which allow subclasses to change the notion of equality. But this trick of using final is likely not appropriate if you release your code publicly and expect users might derive from your superclass - because obviously that prevents the users from being able to change the equals() code of your superclass if they want to remove the limitation.

Hashcode

It should go without saying that, if you are overriding equals(), then of course don't forget to update hashcode() as well. Fortunately, extending hashcode() is much simpler as none of the problems above arise. That's because hashcode() only deals with one object (the receiver), not two objects like equals(), and thus there are no issues with mixed types.

Other languages

I mentioned before I didn't find a sensible way to improve this pattern with the Java language. But this does leave open to consideration how this issue might be addressed and possibly improved in other languages. D in particular comes to mind, due to its meta-programming and generative code capabilities. Mixins for example could be used to alleviate some of the boilerplate that results in the Java case.
Going even further, one can bring this issue to the realm of language design. Perhaps the very way that one defines an equals/hashcode in classes could be changed in order to address this issue just as safely and comprehensively, but in a more pleasant and concise way (D follows the Java system pretty closely, as most OO languages do). But at the moment these are enough considerations for me. Thinking about this issue as applied to D or other evolving languages might be left for another article or discussion in the future, perhaps picked up further by others.

Thursday, 13 October 2011

An indignant reaction to Stallman's comments on Steve Jobs?

Whoa. It seems a lot of people have expressed indignation for Stallman's comments on Steve Jobs. See for example this post, featured in a Slashdot entry: http://www.readwriteweb.com/enterprise/2011/10/why-fsf-founder-richard-stallm.php. I'm surprised by this reaction from the community, and I find it a bit silly.

Surprised because I do not find it unexpected at all for Stallman to make such comments. In fact I would be surprised if he *hadn't* made comments similar to those. And I find it silly for the software community to expect a high level of respect from Stallman for Steve Jobs. It must be understood that in the eyes of Stallman, Jobs was a malignant influence in the software world. It does not matter whether you agree if that was the case or not. A point which seems lost on some people. For example, the post linked above states:
"He [Stallman] manages to offend common decency by celebrating the absence of a man who contributed enormously to the world of computing, and insult millions of Apple users simultaneously."
But in Stallman's view, the contribution of Jobs to computing where very unsavory ones - "evil deeds", in his own words. It wasn't a disagreement in mere preference, execution, or technical issues, but rather on fundamental ethical issues. Frankly, if I believed the same way as Stallman did about a man, I would also not shy of uttering the same words he did, political correctness be damned. (Although I can't think of any such person in the software world)

If one still has to quarrel with Stallman, it should not be with his latest comments, but with his beliefs and causes, the root cause of all his behavior. Beliefs which, of course, are the same as the FSF's - another point which seems lost on the poster, as evidenced by:
"It's time for free software to find a new voice. Once again, Free Software Foundation founder Richard Stallman is putting his feet firmly in his mouth."


Richard Stallman is still the perfect leader for the FSF, in case the poster was implying otherwise. Rather, he seems not to understand the ultimate goal of Free Software Foundation in the first place: that *all* software should be Free. Not merely some, or most of it, but *all* of it. They hold an absolute ideal that says any closed, proprietary software is evil. All that is very clearly expressed in articles by the FSF and RMS. A lot of developers seem to ignore that though, perhaps just because they like the FSF's contribution to Free/Open-Source software - they practically started the whole thing after all, a very good outcome for many people in the software world. But there is a *realm* of difference between merely wanting to have more Free/Open-Source software out there, and thinking that all software should be Free Software. One should not support the FSFs unless one supports the latter idea, simple as that.


PS: is "insulting millions of Apple users simultaneously" against "common decency"? Ehh... isn't that a bit of an exaggeration? I understand the comment might be seen as callous and insulting to Steve Jobs and perhaps those close to him, but millions of Apple users? Apple users are not "part" of Steve Jobs... as much as cult Apple fanboys might think otherwise.

Wednesday, 18 May 2011

Emacs or Vi? Why the answer is neither.

Any programmer worth his salt should, at the very least, have heard of the perennial Emacs vs Vi editor war. And in some likelihood, especially if you are a veteran developer, have a preference, and expertise, in one of these editors. Perhaps you even participated sometimes (or often) in editor discussions, and defended your choice.
And while it is clear there is not going to be a definite favorite in this issue, there is at least the consensus (well, in most of the Linux community at least), that the choice should be between one of these two - Emacs or Vi/Vim - and any other choice is inferior. In practice many people may be doing things differently, they may be using different tools and editors, but the prevailing mindset is still this one. It is however a mindset I strongly disagree with, and this post is a challenge to that. (Brand me a heretic if you wish, but at least bear with me until the end of this post.)

First, some background: I am a very productivity-minded person, in particular a proponent of tools mastery, and therefore, of spending some time to learn and master tools used frequently, to increase one's productivity in the medium and long term. So the issue of which tools are better does matters to me a lot.
When I first started learning and experimenting with Linux, back in 2000, that's when I first heard of these two editors, and how beneficial it was considered to master one of them. I first learned the very basics, so I could get a feeling for both. I quickly took some preference for Emacs, but never went much further than the basics, since most of what I did at the time was small editing of Linux config files. It was later when I was in college, that I started to develop much more. It would then have been the time to master one of these editors and use it for actual coding. But I never did. I used other editors and IDEs (mostly IDEs) and never looked back, I didn't even consider the possibility of making such IDE's editing work like Vi or Emacs. I always felt disregarding Vi/Emacs was the right choice, that they were not good approaches, although I never thought or verbalized much about why that was the case, until recently. Until I became more involved in the D programming language community  (which attracts programmers from all sorts of different backgrounds and languages, not just C/C++ but from Java to Ruby, to Python, Perl, etc.), and related discussions popped here and there, especially as I started to do IDE development.


So what is the reasoning then? Before we discuss the answer, we need to clarify what the question is. That may well be the most important consideration. You see, I think the important question is not "What is the best editor?", but rather, "What is the best development tool for writing software?", where best means, roughly speaking, the most productive. These two propositions are not exactly the same. I don't care much about which tool I use to edit config files, write emails, write notes, articles, etc. . I don't do that often enough to care about being super productive in them, I just need the basics. What I *do* often do is write software and code, so that's what I really care about. And here is where I think that Vi and Emacs are inferior tools. They have two fundamental problems:

First, they are both essentially textual interface tools (as opposed to graphical interface tools), and this is an inescapable shortcoming. Because anything a text UI can do, a graphical UI can do just as well, but the opposite is not true. A graphical UI is simply strictly better, kind of like a Paretto improvement, or an algorithm that dominates another one in all cases and input. Note that I'm not talking about using the mouse vs. using the keyboard here. That aspect is completely unrelated (especially since you can use a mouse just as well in a textual interface). My point is that a graphical interface offers a much greater and better potential to *display and convey information to the user*. For example: font styles, squiggly lines, decorations and other editor markers, images, icons, etc..
Now, yes, both Emacs and Vi have graphical frontends, I haven't missed that. But the thing with those is that they are essentially still a text based UI (see picture example: vi, emacs). Their graphical frontends add something like a menubar, a toolbar with icons, and a mouse interface, but the core editor (and thus the gist of the product) is still text-based. It is far from a full, proper graphical interface, it is still very much constrained in the way I described above.
Another alternative is to modify existing graphical tools (like proper IDEs) to work like Vi and Emacs in their editing mode. But the problem with this approach is that these are extensions, afterthoughts, to the nature of Vi/Emacs. They are not part of the core Vi/Emacs product, yet the extensions will be bound to their paradigm and thus - because these two UI paradigms (text and graphical) will be competing with each the other - the product will never be of a wholesome, integrated design. Well, not unless the extensions breaks compatibility with the standard Vi/Emacs interface, but then it's hardly Vi/Emacs anymore. In other words, if you are designing a tool that is going to be used in a graphical interface, you should design it from the start with that in consideration. Designing it as a text tool, and then have someone else design an extension or additions for graphical interface usage will be suboptimal, inferior.

Now, some people may think the above argument is somewhat moot, or of little value, because they think graphical interfaces do not add that much useful functionality to the development tool. If you convinced of that, you should consider whether that isn't just because you typically work in a environment, an ecosystem, where it is hard (and therefore infrequent) for such advanced functionality to exist in the development tools. On some ecosystems, such as with older languages, that might be common. But more on that later, let's look at the second point.


What is the second reason I think Vi/Emacs are not the best approach? Well, because they are primarily text editors, that is, editors of textual content (note that this is related, but different, to the previous point of whether they have a textual or graphical User Interface). The issue here is that they mostly "see" text content, and their editing operations are textual ones. This is great if what you are editing is indeed little more than plain text (emails, notes, config files). But, if like I said above, what you are doing is writing code, developing software, then it is not that great. Because source code and the underlying languages of that code have a great degree of semantics, and thus, the potential for lots of different semantic operations. Semantic operations that can be just as common, if not more, than textual ones. And as such, since they can be common, they need to be considered with importance in the interface design, for example, in the way they will compete for the shortcut real estate with other operations. If all the best shortcuts are taken for text operations, and little is left for semantic operations, or for that matter, for other language toolchain functionality (like building, debugging), that is clearly suboptimal.
At this point some people may think: 'but really, what other significant "semantic operations" are there other than open declaration or autocomplete?' Well a lot more than you think, if you are asking that question. You probably just don't know it, because your editor doesn't support it. Note that it is not just about operations that modify the source, but also even just the navigation and the display of information. Having more semantics opens the possibility of more functionality for all these aspects (for example, an outline view that displays the top-level declarations of the current file, or icons and decorations for the different kinds of elements your language supports).
But that is an interesting question nonetheless, because it brings us to a secondary point: the truth of "source code and languages have lots of semantics, and thus, the potential for lots of different semantic operations." actually depends a lot on which language and software one is developing with. There is likely a big gap between statically and dynamically typed languages, for example. But even with languages of the same general category, the potential may still be much harder to realize in one compared to another. So if you are working in a terribly broken or convoluted language as say, C/C++ or Perl, don't be surprised if their tools generally don't have much semantic functionality. And the little functionality they have may work poorly, thus reducing its usefulness, desirability and frequency of use. This is what I meant when I mentioned programmers working only in certain ecosystems not valuing graphical interfaces: if all you work with is with such languages and tools, you may never realize the extra potential there is.
For example, for Java the tools are of great quality, like the excellent Eclipse JDT, the Java IDE. I use semantic operations so often in JDT: Content Assist (Ctrl-Space); Open Declaration (F3), quick Type Hierarchy (Ctrl-T), quick Outline (Ctrl-O), Refactor: Rename (Alt-Shift-R or F2 in some other contexts), Refactor: Extract Method (Alt-Shift-M), Refactor: Change Parameters (Alt-Shift-C) Refactor: Introduce parameter (Alt-Shift-T, P), navigate to member/element above/below - Ctrl-Shift-Up/Down), word navigate (Ctrl-Left/Right), and others. Nowadays, I probably use them more often than textual operations - like cut/copy,paste, textual find/replace, etc. (but excluding actual typing of course!). That is why the generality of Java developers (even the good/smart ones - in case you're one of those people who think Java is just for dummy programmers) do not give a rats ass about Vi/Emacs, and rightly so. But my point is that even for other languages, the same holds true: that a better development tool, a graphical one, a proper *Integrated Development Environment* will always be superior in potential to a mere text editor, even a very extensible one like Emacs. And this "superiority" is not an distant theoretical thing, it is something that in practice I would say has been reached for many languages already (although it is a very subjective thing to judge). It certainly has for Java, and even for C/C++ I think it has. So for example, if all I coded was C/C++ I would still not be using Vi/Emacs, but would much rather be using Eclipse CDT, or even Netbeans, Visual Studio, XCode, or KDevelop. This would be true even half a decade ago (perhaps even a full decade ago), despite the semantic functionality and external tool integration of these IDEs not being as rich and featured as it is now. But it still would be better than standard Vi/Emacs, I believe.

This is not to say that there are not good lessons and ideas to consider from Vi/Emacs... surely there are. But it is a very different thing to take and adapt some ideas from a tool, versus actually using the whole tool. (Much like LISP sucks, but it is still good to learn and experiment with :P )
So people, time to move to the new century. We can have a similar discussion again in a few decades, when people start using Kinect-like devices to control their tools, or better yet, Graphical Interfaces get superseded by Neural Interfaces. :)

Friday, 12 November 2010

When later you realize your own code sucks!

Recently, I had my first experience where I looked at some code I had written in the past (roughly 2 years ago) and realized it was quite bad. Bad it the sense you think "OMG, did *I* write this?!". Bad also in the sense that it could have been written in a better way in the same (or less) amount of time that it took to write the bad code. This last point is important because it is common (and somewhat expected) that developers, even good ones, will sometimes write bad code when writing the good code instead would take longer. This situation presents a trade-off of short-term vs. long-term advantages, and deciding this trade-off is a classical software engineering decision.
But that was the case here. Here, the good code was actually quicker to write, I just didn't see it then. The issue was that I had two collections, and the goal was to compare them for equality, but instead of putting the elements in Java's HashSets and then just using the built-in Set.equals(), I stored the elements in two arrays, and checked the equality myself using 2 nested linear searches... So, not only was the code roughly twice as complex, the asymptotic performance was much worse, (quadratic instead of linear). And on top of that, this code was duplicated (with a few minor differences) in another class, something I rarely allow myself doing.
In my defense though, there are some mitigating points:
  • The elements from the two collections were not of the same type. So with the array approach I could directly write the comparison code for the pair-wise element equality while iterating, whereas in the HashSet approach you can't, I had to convert the elements of one of the collections to the same type as the other elements, to be able to use Set.equals() on both sets.
  • This code was part of testing code, not actual application code. Thus the performance issue was not very relevant: it was not present in the application, and the inputs were small, known in advance (because they were specified in the code, not by the user).
I might have not forgiven me if code like this was in application code. :P Or, for that matter, if the code was bad enough that I would not easily understand it anymore, like this. (Exception made for cryptic language features though, like many in C++ ...)