20/10/2010

Macros make the world go 'round (part 1)

This post is a response to Mike Stone's post about closures in Ruby. (Just ignore the rant about the conditional operator; nothing to see there. :-P)

Closures are, of course, useful. I think Lisp languages take closures even further, making them even more useful. How? With macros, of course.

Here's a simple if contrived example: Most math libraries have a log10 function, same as log but using base 10 instead of base e. Now, another useful operation is a base-2 logarithm. Let's call it log2.

Here's a naive way to implement it in Scheme:

(define log2
  (lambda (n)
    (/ (log n) (log 2))))

This is all fine and good, but notice that log(2) is recalculated every time, which is redundant. So, let's hoist the log(2) out of the actual lambda:

(define log2
  (let ((ln2 (log 2)))
    (lambda (n)
      (/ (log n) ln2))))

Heck, you can go even further, and turn the function into a multiplication instead of a division:

(define log2
  (let ((invl2 (/ (log 2))))
    (lambda (n)
      (* invl2 (log n)))))

I could play with the code even further (something I'll explore in my next post), but this will do for now. :-)

The idea with all the above is that you calculate log(2) just the once, and have its value available for only the log2 function, without polluting anybody else's environment.

In order to implement it in Ruby, this seems to be the easiest way I can think of (feel free to suggest something simpler):

class << Math
  define_method :log2, lambda {|invl2|
    lambda {|n| invl2 * Math.log(n)}
  }.call(1 / Math.log(2))
end

This is quite an unintelligible way to define the function: first, we're having two layers of lambda, then calling one of them.

And now, we come to the punchline. :-) In Scheme, the code would actually do pretty much the same thing as the Ruby version behind the scenes, but the outer lambda and the call to it are nicely wrapped up in the let macro. Compare the last Scheme version:

(define log2
  (let ((invl2 (/ (log 2))))
    (lambda (n)
      (* invl2 (log n)))))

with its macro-expanded version:

(define log2
  ((lambda (invl2)
     (lambda (n)
       (* invl2 (log n))))
   (/ (log 2))))

If someone wrote a let macro for Ruby, I think the Ruby version of the code could be similarly pretty.

19/09/2009

Why does nobody play cards? Or, why I love Mensa

A workmate of mine is going on a cruise next week with his girlfriend. In fact, the itinerary was identical to the one I went on about 10 years ago. Ah, nostalgia!

One of the best memories I had of the trip was meeting some interesting people in the card room. I loved playing card games, and was glad to find people to play 500 and Euchre (and a little Bridge) with. I could seldom find people in my life to play card games with (even The Ferret doesn't play those games, sadly), so this is a big deal for me. :-P

(The other thing I really loved about the trip was all the yummy lobsters they served in touristy areas—way expensive, but way worth it for a seafood lover like me. But this post isn't about seafood, so. :-P)


Anyway, years later, I joined Mensa, mostly due to a dare that my best friend struck with me. She was studying intelligence testing for her psych degree, and did an online intelligence test and scored highly for it, so she asked me to do it to see how my score compared with hers. We both scored similarly.

But you know, online tests don't tell you very much. So I said to her, maybe you should do the Mensa test, and if you get in, you'll know how smart you are! She was…self-conscious, not wanting to face the prospect of flunking the test. So I said, how about we both do it, and see who gets in! I fulfilled my end of the bargain. She, to the best of my knowledge, still owes me. :-P


Anyway, Mensans come in all shapes and sizes, and while stereotypically, non-Mensans probably think of Mensans as people who yabber about quantum physics and rocket science all day, really, I don't go to Mensa gatherings for any of that. No, I'm there for something much more intellectual. :-P

At least in New Zealand, Mensans play card games. I kid you not.

So we'd stay up till 3 am playing 500 and (when we stop being able to be bothered to concentrate) various other card games. It's my favourite part of Mensa gatherings, much more than the talks or whatever else they plan in their official schedule.

Anyway, I'm in the US now (why, how things change in the nine months since I last wrote!). Next year, there will be a joint American/Canadian Mensa gathering, which I intend to attend. May there be lots and lots of card games!

5/12/2008

InformIT, the new addiction

As of current writing, I've currently purchased 6 ebooks from InformIT, Pearson Education's IT book portal. It's seriously addictive! There're two very good reasons why this is.

  1. Their newer ebooks are non-DRM, straight PDFs. You can view them using any PDF reader; on my computer, this happens to be Evince, but anything else should work. This is much akin to MP3: 100% compatible; just as you can play MP3s on all music players, you can view PDFs on all document viewers.
  2. When you first sign up, you get a 35%-off coupon to spend at their store; every purchase you make, they give you another 35%-off coupon for your next purchase. This incredibly clever marketing technique means that you'll keep coming back to their store when you want more books (at least ones published by Pearson's companies); in many cases, they even undercut Amazon.

So next time you're hunting for a computer book, you know where to go! :-)

30/11/2008

On idealists and idealism

Let me say up front: I'm an idealist, and I'm proud of it. It's a central part of my identity, and I'd be totally lost without it. Idealism, to me, is believing in doing something because you should, because it's right, not because it's expedient or pragmatic. Sure, practical concerns do affect our daily lives—for example, we can't spend more money than we have—but where we have a choice in what we do, idealism means that we do what's right for everyone around us, for the world around us, for the bigger picture.

It's nice to serve ourselves, and we certainly shouldn't help others at the total expense of our wellbeing, but the opposite is (in my idealist eyes) far worse. In that picture, everyone is just out to serve themselves, with no regard for others; they'd slay their own parents if it meant they'd get their inheritance a few years sooner, for example, or their spouse so they can fraudulently cash in on their life insurance.

The latter is not a world I want to live in. I've already seen too much evil in my years on this earth, and it's burdened me with a fair sense of cynicism. However, I refuse to let that cause me to allow the world to get any worse. No matter how much of a dead end we feel we live in, we must strive to make things better, in whatever way we can. No amount is too little, as long as we can honestly say we did our best.

It is for that reason that I have voted (since I was 18), and continue to vote, for the Green Party, that I support Richard Stallman and Dennis Kucinich, and why I'm married to The Ferret, who, though not as high-profile as the people I just named, is a shining example of how idealists should live their lives.

It is also the reason why I speak up for causes I care about. One of the things I care about is that companies should get their priorities straight, which is to serve customers first, and not just as a means to profit. In fact, I personally believe that running a business just to profit (with other reasons being subservient to that) is dubious; it says you've sold your soul to money. Profit should be a means to other (hopefully more ethical) ends, and not the end to which other considerations are merely means. The profit-as-end companies are those that will gladly fire their most talented staff if it were expedient for them to do so, such as if said people started whistle-blowing. Clearly, that sort of company would be a poor place to work for if you value social justice.


Recently I had a run-in with some misguided people who have their priorities the wrong way around. One, I believe, even accused me of “shit-stirring”. (Thankfully, since they're posting anonymously, they can't go and redact their comment, and I certainly have no inclination to remove evidence of people's, umm, ‘logical slip-ups’.) I believe, in that poster's eyes, any kind of ‘heretical’ ideas (such as the Earth being round, or that evolution created the earth's many varied lifeforms, to name two historical examples) are shit-stirring. Well, in that case, I'm glad to be among that crowd.

Shit-stirring is how the world progresses! How on earth do you expect our society to give women the right to vote, to abolish segregation, to legalise abortion and (in New Zealand) prostitution—and hopefully soon, gay marriage and cannabis—if there weren't people who spoke up about it?! If people don't put their foot down on things they care about, how does this world improve? By sitting on our arses and excusing companies' and governments' misbehaviour? Surely you jest!

Also, just because a company ‘invented’ a technology does not give them the (ethical) right to piss on others, whether others be their customers, competitors, or anyone else. Even if the law doesn't require it, we all owe it to the society and world around us to be better than self-serving. Much, much better.

But since we're talking about innovation, who do you think invented Firefox, or BitTorrent, or other such great technological wonders of the Internet age? Is it a certain company in Cupertino? Bzzzt! The world does not revolve around them, contrary to the apparent opinion of some of my respondents.

As we all know, they're inventions of some of the best free software hackers in this world. Likewise with many other pieces of free software that's in daily use, all around the world, created by people who believe, one way or another, that giving their users freedom is the right thing to do. Off the top of my head, I can name: Emacs, Apache, TeX (and LaTeX), PHP, Ruby (and Rails), Java (although it's had a funny kind of history, in the free software world), and Mozilla (the progenitor of Firefox). These are all original works, not clones of proprietary software.

Luckily for proprietary software supporters (and social justice in general), free software does not allow discrimination against certain groups of users, and they can all benefit from these works. I wish I could say the same of non-free software.

So, am I shit-stirring enough yet? Glad to be of service!

29/11/2008

A curry a day keeps the doctor away

As I'm writing this post, I'm very sniffly and otherwise not doing very well. And I'm reminded of my usual cure for such ails: spicy foods! I find that spices make me sweat all the nasties away, and I end up feeling much better the next day.

I especially love hot Indian curries, such as vindaloo. Although in New Zealand it's customary to serve vindaloo with beef, I find that I like lamb vindaloo a lot. There's also an Indian place around here that serves an item they call goat curry; it's one of my favourite dishes, especially if I order it ‘Indian hot’ (as opposed to ‘Kiwi hot’, which is more like medium heat).

Right now, though, just past midnight, there are no Indian restaurants open, so I have my next best fix, which is instant Korean noodles. That's one thing I love about Korean food: it's spicier than most other kinds of East Asian cuisine (except perhaps Sichuan, which I also love). You've got to hand it to those northeners; the southern cuisines (e.g., Cantonese) tend not to be nearly as spicy. I know I'm making huge generalisations, but in terms of Asian foods available in an English-speaking country, I dare say I'm not too far off the mark.

I'm just now reading about the phall, which I've not seen in New Zealand, but which I'd love to try! Bring it on! :-)


Well, this is it! I've come to the end of NaBloPoMo, with a post a day! Sure, some posts were more content-free than usual, and some, well, rather controversial, but at long last, it's done! I have one more post in my blog topic queue, which I might post tomorrow if I flesh it out by then, but in any case hopefully sooner rather than later; after that, I don't suppose I'll have anything to blog about for a while. I don't intend to do NaBloPoMo for December.

28/11/2008

The law of gravity

Whether you're tramping up a hill, or kicking a ball, or whatever, one of the first things you learn is that things come down much more easily than they go up. That's gravity for you!

I've come to appreciate exactly the same phenomenon for rechargeable batteries. Today, I've been setting up a brand new UPS to power three laptops and a printer; I've read from HP support that it's best not to leave a laptop battery in the laptop if you're running your laptop for long periods of time, so, in order to do that and still have some protection against power outages, I felt that a UPS is the best option (not to mention that it helps protect against power surges, which have killed more of my computers than I want to think about).

Anyway, as a test of the UPS-monitoring interface (if you're an Ubuntu user, the nut-hal-driver package is all you need, if your UPS has a USB connector like mine does), I decided to unplug the mains for a while, and see how long things would run. The system estimated about 20 minutes of run time. When the UPS got to about 70% charge (after about 5 minutes), I plugged it back in. As of current writing, it's been charging for forty minutes now, and it estimates taking another twenty minutes to finish charging.

Yeah, you definitely don't want to be having power outages too often, UPS or no. :-)

Update: it's now been charging 75 minutes now, and still estimates 20 minutes to finish charging. This is starting to look like software development schedule estimates! :-P

The Simpsons says it best

I was trying to come up with a topic to blog about today, when a friend kindly posted a link on his Facebook profile about the shenanigans Apple has been getting up to lately, what with trying to lock all iPod users to using iTunes, by threating lawsuits against hosting companies whose clients include makers of any software that's iPod-compatible. My vendetta is made rather more personal because one such maker is a friend of mine, and his software, El Tunes, has been targeted by Apple's hitmen too.

I see too many software geeks using MacBooks and other Apple gear, and in so doing elevated them to a geekier status than they deserve. (Apple products, geeky? Surely you jest!) That must stop! It's trendy in geek culture to trash Microsoft and wish for its downfall; I feel that the same trendiness in trashing Apple must come forth in the geek world too. I'm no fan of Microsoft (I'm primarily a Unix coder, after all), but it's quite something for me to hate Apple even more than I hate Microsoft.

To whatever degree I (or my real-life persona :-)) will gain some voice in the geek world in future, I will not hesitate to give Apple some of the same ‘love’ that Microsoft has been getting, nor will I be anything less than encouraging to other geeks who wish to spread said ‘love’. Let me start by recommending, if you're on Facebook, the I hate Apple group.

Yes, this is a declaration of war: they deserve it, every little bit.

Update: Getting swarmed by fanboys! Oh noes! :-P

Update: The Simpsons episode with the Mapple store is totally hilarious, says what I've wanted to say much better than I ever can. :-) And, what's funnier, the way some ‘Mapple’ fans make fools of themselves on this thread are priceless! (And not very much different from the ones on this thread. :-P)

27/11/2008

Serendipity? (Part 4)

Some readers may know of my recent fun getting my laptop, and getting it up and going. I've been using the laptop some more now, and I'm finding more and more things to like about it and its setup. It's a really neat laptop, and I consider it quite a bargain.

I finally got around to setting up Ubuntu 8.10 on it. Despite my struggles with its installer, I now have a fairly ‘standard’ crypto setup, wherein hibernation actually works (after a couple of false starts). This is a biggie to me: I run my last laptop 24/7 because there isn't a good way to hibernate it, and this is not good for the laptop's longevity. Heck, my last laptop is only 11 months old, and it looks twice its age.

What I mean by ‘standard’ crypto setup is one that's quite similar to that created by the Ubuntu installer: namely, a big crypto partition that houses an LVM physical volume, within which you set up all your logical volumes. This is quite an improvement on the old, hacky way I set up crypto on my laptop, which was to set up a big LVM physical volume, with each logical volume encrypted separately. The old setup is bad because that means that the LVM data structures are unencrypted, and also that the logical volumes are not transparent: you'd have to know to decrypt them on each use. Evidently, too, it is also bad enough to prevent hibernation from working.

Both my old and new laptops have nVidia graphics cards in them, though the old one has a (apparently) dinky GeForce Go 6100, whereas the new one has a GeForce 8400M GS. This means that while the old laptop can't really do a very good job of using Compiz (at least without freezing the computer on occasion), the new one just breezes through it.

I've installed VMware Workstation 6.5 on the new laptop (since getting 6.0.5 to work on Ubuntu 8.10 is a huge pain, you'd have to hack vmmon to make it work with 2.6.27 kernels), and it's neat (even though I detest having to use their installer). I can run two virtual machines simultaneously without having one stall the other excessively (as has happened with the old laptop); this is a big deal given that:

  1. They both use a fair amount of memory (2 GB and 1 GB respectively)
  2. I run them with all memory allocated in host memory; this is because the virtual machines are fairly disk-intensive and I don't need VMware's memory swapping to add to that load
  3. Both laptops has just 4 GB of RAM (since it only has two DIMM slots, and 2 GB DIMMs are the biggest affordable type you can get; 4 GB DIMMs are hugely expensive)

Oh, but on using virtual machines, the most useful thing about the new laptop is that the wireless card (Intel PRO/Wireless 4965, as opposed to the old laptop's Broadcom BCM4311) supports promiscuous mode. This means I can have guests use bridge mode networking; previously, I'd have to set them all up to use NAT mode.

So yes, that's my initial impression after a day of using the new setup on the new laptop. So far, I must say I'm very impressed; I look forward to more pleasant surprises! :-)

26/11/2008

Buffets and eating habits

What is it with people who order more food than they can eat? Now, I understand that for à la carte dining, you can simply take your food home and eat it later (although actually remembering to eat the leftovers is another story). But you're not really supposed to take buffet food home (at least without paying more for it). Some buffet places will charge you more if you leave food behind; I think it's a great idea. People should not be encouraged to waste food in general, but when it's at the restaurant's expense, this should be doubly discouraged.

My point about eating what you've ordered is not about stuffing food down your gullet and gaining 50 kg in the process. I don't advocate overeating in general, and definitely not in a buffet context (I say this as someone who had overeaten at buffets too often in the past). The idea of a buffet is that you can choose how much to put on your plate. Most good buffets let you go on multiple trips. Make use of it!

By which, I don't mean stuff your plate on the first go, then the second, then the third, and so on (unless you actually want to eat that much food, and can fit it all in). I mean, put a little on your plate each time, and top up while you're still hungry.


I'm now going to go off on a rant about parents who make kids eat everything on their plate, and give them huge complexes about it. This is, in my rather arrogant opinion, a terrible habit to teach kids. Kids should learn not to waste food, yes, but the lesson they should learn is to not bite off more they can chew (and in fact, when they do order more than they can eat, they should learn to keep the food overnight to eat another day), rather than that they should eat everything, as such. The latter approach will make them overeat, then discover that they're getting much, much fatter than they want to be, and (later on in life) start leaving tons of food on their plates to compensate for the emotional trauma they've gone through.

Remember, less is more. There, I'm done ranting. :-)

25/11/2008

In JIT we trust, part 2

One of the things I love about dynamic compilation is that it frees me from having to think about the minute details of how some piece of code will be compiled. In fact, the effects of hand-written ‘optimisations’ are less predictable than one might expect!

Take this simple example: which one do you prefer between these two?

  1. for (i = 0; i < array.length; ++i)
  2. for (i = 0, len = array.length; i < len; ++i)

I've been guilty, for a long time, of picking the second one, on the premise that if the length operator didn't have to be called all the time, it'd be faster.

That's the biggest pack of lies ever.


The punchline is that both are about the same in terms of performance, counter-intuitive as that may be. I wrote a little test program, which I tested on my system (Java SE 6, update 10, 64-bit server VM). All it does is print all the command-line arguments to System.out, a million times.

My code tests this loop under a number of different execution scenarios: checking the array boundary the ‘naive’ way (i.e., checking the array length at each run); hoisting the array length and checking that; using for-each (aka extended for loops); as well as hoisting System.out for each of those test types. There's also a ‘super hoist’ mode that does all the above hoisting, as well as doing the million-run loop by hand rather than using a range iterator (see below).

To minimise other system effects, I wrote a null output stream (which acts like /dev/null, without actually opening any device), a range iterator (that always returns null, rather than the iteration count, to avoid boxing overhead), and a no-op loop (just to see how much overhead the loop itself is creating). To minimise warm-up effect, the whole suite is run three times.


Here are some test results for a run with 25 empty arguments. I picked empty arguments to minimise any string-copying overhead that may exist; after all, the point here is to test the various array looping techniques, not string copying!

Timing results (all times in seconds)
Loop typeRun 1Run 2Run 3
Naive11.33810.45510.460
Length-hoisted11.78611.51911.299
For-each11.52111.51711.141
Out-hoisted naive11.41911.46511.184
Out-hoisted length-hoisted11.51411.47511.168
Out-hoisted for-each11.73011.51911.512
‘Super hoist’11.61411.53912.045

If anything could be considered ‘faster’, I'd have to say it's the naive loop! However, the differences are too minute to be meaningful.


Code listing:


import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class PrintArgs {
    static final int RUNS = 1000000;

    public static void main(String[] args) {
        System.setOut(new PrintStream(new NullOutputStream()));
        runSuite(args);
        runSuite(args);
        runSuite(args);
    }

    private static void runSuite(String[] args) {
        time(new NoopLoop(args));
        time(new NaiveLoop(args));
        time(new NoopLoop(args));
        time(new LengthHoistedLoop(args));
        time(new NoopLoop(args));
        time(new ForEachLoop(args));
        time(new NoopLoop(args));
        time(new OHNaiveLoop(args));
        time(new NoopLoop(args));
        time(new OHLengthHoistedLoop(args));
        time(new NoopLoop(args));
        time(new OHForEachLoop(args));
        time(new NoopLoop(args));
        time(new SuperHoistLoop(args));
        System.err.println();
    }

    private static void time(Runnable runnable) {
        long t0 = System.currentTimeMillis();
        runnable.run();
        long t1 = System.currentTimeMillis();
        System.err.printf("%s: %d ms%n", runnable.getClass().getName(),
                t1 - t0);
    }

    static class NullOutputStream extends OutputStream {
        public NullOutputStream() {}

        @Override
        public void write(int b) {}

        @Override
        public void write(byte[] b) {}

        @Override
        public void write(byte[] b, int off, int len) {}
    }

    static class Range implements Iterable<Void> {
        private final int start, end;

        public Range(int start, int end) {
            this.start = start;
            this.end = end;
        }

        public Range(int end) {
            this(0, end);
        }

        @Override
        public Iterator<Void> iterator() {
            return new RangeIterator(start, end);
        }
    }

    static class RangeIterator implements Iterator<Void> {
        private int start;
        private final int end;

        public RangeIterator(int start, int end) {
            if (start > end)
                throw new IllegalArgumentException();
            this.start = start;
            this.end = end;
        }

        @Override
        public boolean hasNext() {
            return start != end;
        }

        @Override
        public Void next() {
            if (start == end)
                throw new NoSuchElementException();
            ++start;
            return null;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    static abstract class MainRunnable implements Runnable {
        private final String[] args;

        protected MainRunnable(String[] args) {
            this.args = args;
        }

        @Override
        public void run() {
            for (Object _ : new Range(RUNS))
                main(args);
        }

        protected abstract void main(String[] args);
    }

    static class NoopLoop extends MainRunnable {
        public NoopLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {}
    }

    static class NaiveLoop extends MainRunnable {
        public NaiveLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {
            for (int i = 0; i < args.length; ++i)
                System.out.println(args[i]);
        }
    }

    static class LengthHoistedLoop extends MainRunnable {
        public LengthHoistedLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {
            int len = args.length;
            for (int i = 0; i < len; ++i)
                System.out.println(args[i]);
        }
    }

    static class ForEachLoop extends MainRunnable {
        public ForEachLoop(String[] args) {
            super(args);
        }

        @Override
        public void main(String[] args) {
            for (String arg : args)
                System.out.println(arg);
        }
    }

    static class OHNaiveLoop extends MainRunnable {
        private final PrintStream out;

        public OHNaiveLoop(String[] args) {
            super(args);
            out = System.out;
        }

        @Override
        public void main(String[] args) {
            for (int i = 0; i < args.length; ++i)
                out.println(args[i]);
        }
    }

    static class OHLengthHoistedLoop extends MainRunnable {
        private final PrintStream out;

        public OHLengthHoistedLoop(String[] args) {
            super(args);
            out = System.out;
        }

        @Override
        public void main(String[] args) {
            int len = args.length;
            for (int i = 0; i < len; ++i)
                out.println(args[i]);
        }
    }

    static class OHForEachLoop extends MainRunnable {
        private final PrintStream out;

        public OHForEachLoop(String[] args) {
            super(args);
            out = System.out;
        }

        @Override
        public void main(String[] args) {
            for (String arg : args)
                out.println(arg);
        }
    }

    static class SuperHoistLoop implements Runnable {
        private final String[] args;

        public SuperHoistLoop(String[] args) {
            this.args = args;
        }

        @Override
        public void run() {
            PrintStream out = System.out;
            for (int i = 0; i < RUNS; ++i) {
                for (String arg : args)
                    out.println(arg);
            }
        }
    }
}

24/11/2008

Serendipity? (Part 3)

Remember how I said that the DVD drive on my laptop was busted and that I'd follow up on it sometime? Well, the main reason it bugged me was that I wasn't able to burn a clean copy of my recovery discs, which obviously is a huge concern of mine should I need to bring the system back to a factory-original state.

So, I ordered a set of recovery discs ($30—it would have been free under warranty if I could provably demonstrate that my DVD drive is busted, but as I mentioned in my last post, that's not easy, and I figured $30 is a small price to pay for peace of mind), and received them today. There are two discs: a System Recovery DVD, and an Application and Driver Recovery CD (the latter has 10 MB of content).

As opposed to normal recovery discs, which are simply a snapshot of the system at initial installation, these discs contain just program files. As such, it has to install everything from scratch, and is, in my case, a five-hour-long recovery process. But, at the end of this process, a program called PC Angel creates a snapshot of the entire system image into the recovery partition for quick recovery; once that is all done, I have a laptop setup that is identical to how I expected it! Colour me impressed!

23/11/2008

Short and sweet

I just want to say that WCPE is still my favourite station! They have an amazing selection of music, and is the perfect coding companion (yes, I've been too busy coding to write a real blog post)! If I ever move to the Raleigh, NC area, I look forward to listening to them all day long. :-)

(Note to self: fulfil the pledge I sent in earlier this month!)

22/11/2008

Serendipity? (Part 2)

Remember the new laptop I got, the ex-demo one that sold at $100 discount? It's mostly good, but the DVD drive is busted. I have some theories as to why, but won't speculate.

I called up HP about it, but the service people there aren't particularly happy to look at it if it “works” (which it does, but there are intermittent read failures, and the speed of disc access is atrocious). At the moment, I'm writing a program to demonstrate the bustedness of the DVD drive in a more reproducible way. Hopefully, once that's done, I'll have much more grounds to send the laptop for warranty servicing.

I'll post the code in a future post, if it does do what I intend….

21/11/2008

Syntactic sugar makes the world go 'round

For a long time, I considered Java the C of the Java platform. It's pretty much the lowest-level language you can write code with, unless you're using assembly language (and I remember playing with Jasmin in the early days of Java).

Back in early days, the Java language has very little in the way of syntactic sugar, much like C, except that in C you have a preprocessor, whereas in Java, you had to hack your own (just look at the OpenJDK source code for ample examples of custom preprocessing tools in use). So all the code is ultra-verbose, and (to me) there was a huge mental barrier to writing code of any size, just because I had to spell everything out, every time. (Just don't get me started on the lack of typedef in Java.)

With what syntactic sugar there was, it was too easy to encourage programmers to Do The Wrong Thing™: the availability of operator += for strings meant that a lot of programmers tried doing string-building “the C++ way”, never knowing the difference in semantics between += in C++ versus Java. My feeling on this is that += should not be defined for strings, only on StringBuilders and StringBuffers, with the same effect as calling append() but without the function call syntax.

Of course, once we go down that road, perhaps we would need to provide operator overloading across the board. That, of course, leaves me asking why we don't just use C++ in the first place, perhaps doing a variation of C++/CLI for the Java platform. Oh wait, the Java platform isn't as accommodating as .NET is non-Java concepts; I just can't imagine how, say, the STL would be implemented on the Java platform.


As of Java 5, there were certainly a lot more syntactic sugar for things that were commonly done ‘the hard way’ in the past; type-safe enumerations and generics are two that come to mind, not to mention ‘real’ for loops and variadic functions. And soon (perhaps by Java 7, perhaps not), there'll be closures, which I also welcome.

After all these changes, would Java then be seen as the C++ of the Java platform? (Sorry, this is not meant as a slight to C++; I know that Java does not even come close to approaching the power of C++, but it's the best analogy I can come up with.) Is this actually worth pursuing, or should we all start flocking to more powerful languages, perhaps to Groovy?

I singled out Groovy for one reason: it was a language designed for the Java platform, and has the greatest chance of fitting snugly within the Java platform. Of course, there are things it needs to implement, like annotations, but all in good time. My feeling is that, be it Groovy or some other new language, it has to be a language that allows you to express anything you can in Java; and it has to also allow more powerful ways of expressing them, as well as things that cannot easily be expressed in Java.

I would love nothing more than for programmers to write most of their code in a high-level language, with the bottlenecks separated out into a separate module, which can be written in a lower-level language like Java, for performance. But to get people to switch, that high-level language had better be good to use, and not compromise on features that are available in the Java language.


So, the whole point of that little ramble is that I do think there needs to be a ‘better Java’ for Java programmers. Everything that's easy in Java should stay easy, and anything that's commonly done in Java (and programming in general) but is hard should also be made easy, to further encourage their use. Also, it should link to Java in a seamless way; it should be easy to incrementally convert a project to this new language, file by file.

The language can use either static typing (provided there's good type inference so programmers don't have to spell out types all the time) or dynamic typing; I don't care either way, especially given developments in Da Vinci Machine that will give dynamic languages much better support on the Java platform. I have a feeling, though, that experienced Java programmers will continue to prefer static typing.

The language I envision does not need to be backwards-compatible with Java; for example, the whole insane syntax for anonymous classes can be done away with, especially in one-method cases. It also does not need to function in any platform other than the Java platform. I don't really care about portability to unmanaged platforms or to .NET or to Parrot or to anything else.

Really, like I said, I'm just looking for a ‘better Java’ that's good for most present Java programmers to switch to. And, given the current wide(ish) adoption of Groovy, this ideal language would probably be implemented as a future version of Groovy.

20/11/2008

Foodstalgia

I know that's not a word, but whatever. Right now, The Ferret and I are trying to decide what to eat for dinner, and it just reminds me of all the foods I loved and can now no longer find. What about you? What foods do you have fond memories of, that you can't get anymore? Are you as devastated about it as I am?

My current list is as follows (these are all New Zealand-specific, sorry to foreign readers!):

  1. Georgie Pie (both their pies and their fish'n'chips)
  2. BK Big Fish
  3. Chocolate Frosty
  4. Cherry Coke
  5. Weis ice cream bars
  6. Mooloo ice cream

Sad yet? I sure am….

19/11/2008

Even expert mode ain't expert enough

Some diehard geeks I know consider Ubuntu to be too softcore for their liking. However, it seems a reasonably good compromise to me: I get new versions every six months, instead every 3 or so years (Debian stable), or every week or so (Debian testing). I've used Ubuntu for three years now, and am quite happy with it. (Prior to that, of course, I was a diehard Debian user. I can't bring myself to use any Red Hat-derived system; it's far too different from the Debian way of things.)

Anyway, control-freak Ubuntu users know to use the alternate (as opposed to the live desktop CD) for setting up their systems, as well as the picking the expert mode option; this allows the user to make decisions on a much larger variety of options than the system usually bothers the user with. Still, there are things that I wish the installer allows me the choice of, so I don't have to fight it so hard.

  • When encrypting my hard drive, I use the cipher specification aes-xts-plain or twofish-xts-plain. But the best choice of cipher mode that the Ubuntu installer allows (as of 8.10) is CBC. That is a long, long way off from XTS!

    So instead, I get to rerun cryptsetup luksFormat after the installer creates the CBC-mode partition (if the installer doesn't get to create the partition first, it gets seriously confused), just so I can have XTS mode. Great!

  • The whole installer uses decimal gigabytes (10⁹ bytes) instead of binary gigabytes (2³⁰ bytes), which is quite frustrating to a programmer who's used to the latter. If I want a 10 (binary) gigabyte partition, I should be able to type 10 GiB, and not be told “Invalid size” and made to type 10.73741824 GB instead.

    So instead, I get to create all my partitions by hand, running fdisk and lvcreate manually. Great!

  • The installer does not let you pick your uid/gid, always using 1000. So instead, I get to edit that afterwards:

    
    sed -i s/1000/$UID/g /target/etc/passwd
    sed -i s/1000/$GID/g /target/etc/group
    chown -R $UID:$GID /target/home/$USER
    

    Great!

18/11/2008

Sometimes I wish I were a cop…

…just so I could bust all the bad drivers out there. The amount of bad driving I see, especially in Auckland, is phenomenal! By far, my biggest peeve on the road is with people who are too lazy to signal. It's not that hard to flick your signalling stick—especially when you're leaving a roundabout!

That little ‘courtesy’ (it's a legal requirement, but a vast number of Jafas seem to be wilfully ‘ignorant’ of that) means that people can move through roundabouts much faster—as soon as one signals out of the roundabout, other drivers can enter it straight away, safe in the knowledge that one isn't going to end up in their path (unless one is stupid and changes one's mind about leaving the roundabout—but that's another rant for another day).

16/11/2008

Item 52: Refer to objects by their interfaces

(A side related note first: I still need to get the 2nd edition of Effective Java. The 1st edition copy I have is very well-read, though! Highly recommended for all Java programmers. Edit: I did buy the book in the end; InformIT sells non-DRM PDF copies, so yay!)

Anyway, this is a short rant about one of my personal peeves. To put it succinctly, I'll borrow a line out of Effective Java: Item 52: Refer to objects by their interfaces. I believe this so strongly that I've used that as the post title too. :-)

Let's motivate with an example:


Vector<Number> getPrimes(Number from, Number to);
Collection<Number> getPrimes(Number from, Number to);

Which one of the two would you prefer for your interface? Without reservation, I'd prefer the second one, because it doesn't name a concrete return type, thus giving the implementation more flexibility; for example, instead of having to create a Vector, you could use sets, singletons (from Collections), arrays (with a List view created by Arrays.asList), or even a custom type, as long as it fulfils the Collection interface. Clients that want to use Vector could simply construct one from the returned collection.

The same applies to incoming parameters in an interface too. For example:


Number sum(Vector<Number> numbers);
Number sum(Collection<Number> numbers);

Which one do you prefer here? (It could be argued that List should be preferred to Collection if the order of the numbers is significant; however in this example of summing, addition is commutative so this doesn't matter.) To use the first signature would put a burden on your interface's clients; there is no easy construction syntax for making a Vector, whereas on the other hand there are easy ways to express creation of lists (Arrays.asList is variadic), singletons, etc.

In summary, where there are interface types you can use for incoming parameters and return values, you should definitely prefer them. Concrete types are for instantiation only, or perhaps for use in legacy hierarchies that don't have interface types. (For instance, I consider it totally broken that Reader and Writer are abstract classes instead of interfaces.)

Serendipity? (Part 1)

Last night I was looking at the Dick Smith website, looking for a special on a laptop I could use with hardware virtualisation (this is not just a performance issue—my work is done in a 64-bit virtual machine, and it's impossible to run 64-bit guests without HV—on VMware, at least for Intel processors; and on VirtualBox, across the board).

I found one, the HP Pavilion dv5-1004ax, which, being an AMD system, would fit the bill just fine. Only one problem: they're very short on stock on them, and they're not really generally available anymore. But the Sylvia Park branch apparently had 3 available this morning (which means, in practical terms, at most one new one; the others are demos and returns). The salesperson on the phone said he'd hold it for me, so I thought I'd go there and see.


When I turned up, the salesperson asked me to wait while he fetched the laptop for me. When he came back, about a quarter of an hour later (just how long does it take to retrieve a unit?!), I noticed that the box did not have the HP seal on it, and I queried him about it; what happened to the new one?

Apparently sometime earlier in the morning, someone bought the last new one, and he ‘thought’ that this one was new too, just that there was a customer who demanded to have a look at the unit, and that was why it was opened. I said, okay, let's have a look at the laptop; I could at least see what the state of the laptop bag seal is (if the laptop bag seal was intact, I'd treat it as completely new).

However, not only was there no laptop bag seal, there was no laptop bag at all! I thought that was highly suspicious, and told him so, and asked him what happened to the bag. He said there was none. Yeah, right! I've bought many laptops before (we currently have 7 at home, and most of them are HP), and I know better. Okay, let's have a look inside the laptop.

Two things were instantly obvious. The touchpad was full of fingerprint marks, and the screen was coated with dust. That meant only one thing: it was a demo unit! I was not at all impressed that he was trying to sell me a demo unit as new, without any discount. He said he'd talk to the manager to see how much he could knock off the price.

In the end, the most he could do was $100; he said that the manager normally only discounted $50 for demo units, and that the $100 discount was a special dispensation over the fact that I came here on the understanding that a new laptop would be available. I thought, yeah right, whatever; I was seeking a $200 discount. But I figured I'd talk to The Ferret and see what she'd say about it all.


When I went back to tell the salesperson that no can do, he was tied up with other customers, so I thought I'd look around and see what other laptops there were around. One of the ones I spotted, also selling for cheap, was the HP Pavilion “Thrive” SE dv6723tx; it was much like the “Influx” SE dv6819tx, but with a T7500 processor (which is exactly what I needed). So I asked another salesperson (let's call him salesperson 2) about it.

He told me that that was the last unit left; there are no new ones, anywhere (which I know to be a fact). So I asked him what he can do for me, in terms of discount. Initially he offered me extended warranty, and a ‘starter pack’ thrown in for free, but neither of these things did anything for me, so he offered a $100 discount. This was acceptable to me (hey, the starting price wasn't bad to begin with, and nobody tried to pass it off as a new laptop), but I'd talk to The Ferret about it first, anyway.

Upon my return, salesperson 2 was tied up with another person, and the manager (whom salesperson 1 spoke with) came by, and I explained that I'd like to buy that laptop, with the $100 discount that was agreed to. The manager looked shocked; he couldn't believe that a $100 discount was being offered, and explained that normally with display units, the discount is $50. Well, not my problem. (At $50 discount, I would have been much less willing to part with my money.)

Salesperson 2 then returned, glad that I was back to place the order. However, the manager wasn't nearly as glad that the $100 discount was being offered; it turned out that, because the laptop was already on clearance discount, the $100 discount would put the sale price at parity with the cost price. I felt really bad for salesperson 2, especially because if their staff work on a commission basis, he'd get $0 from that sale. However, far be it from me to complain about the price!

Salesperson 2 was very good-natured about it though, and was very helpful for the whole transaction. So there are definitely good people at the Sylvia Park Dick Smith; don't let my earlier section dissuade you from shopping there!


Anyway, I'm pretty pleased with my new purchase, and with the excellent service I got from salesperson 2; I don't yet have time to play with it, what with all the work I currently have, but The Ferret is drooling all over it (I said to her that if she liked the chassis of the “Thrive” one better than the “Influx” one, I'd be willing to swap with her, after swapping the CPUs). I look forward to giving it a go in a couple of days!

15/11/2008

When you buy Intel, you get what you pay for

After all that drama with trying to get cheap laptops on specials, it's time to return them! At least most of the ones I'm returning are unopened, so they can resell them ‘as new’.

Okay, okay, the one I got from Noel Leeming (HP Pavilion dv6914tx), that was my bad, for not checking out on the Core 2 Duo specifications on the CPU first. That CPU (T5750) was listed in the table, as one that doesn't support hardware virtualisation.

But I also took advantage of a couple of other specials too (HP Pavilion “Influx” SE dv6819tx, and Sony VAIO VGN-NS15G), and their CPUs (T5850 and T5800 respectively) aren't even listed! How could I know whether a CPU supports hardware virtualisation if it isn't listed?!

Now, reading up on the web revealed that I could replace the CPU (the HP manuals go into great lengths to explain how to accomplish this, so it's obviously a supported procedure), but the only comparable CPU currently available, a T7500, will cost me $400 to get, which would completely negate the savings I made from the specials, and put me back to square one.

In comparison, my current laptop (which I got for even cheaper), a Compaq Presario F573AU (with an Athlon X2 TK-53 processor, i.e., not even a Turion), is the dinkiest laptop of all the ones we have at home; it also happens to be the only one that supports hardware virtualisation (being the only laptop here with AMD instead of Intel).

So, that's why AMD rocks, and Intel can eat a donkey or something. :-)