Tuesday, March 15, 2011

Scala vs. a Java Anti-Pattern

In the last few months I've been playing with Scala, a new(ish) programming language that runs on the JVM. The problem with learning a fun new language is that you start wanting to use its features, even when you're still using a different language. (In this case, Java.) But I came across a case this past week that illustrated this so well that I actually wanted to write it down to share.

The Anti-pattern

It's common to need to return more than one item. Maybe a name and date, or an ID and a status, or a result and an error code. Languages like Java don't really make this easy -- the return statement only takes one argument.

This often gets solved by returning some type that approximates a tuple. For example:

// ...
TuplePair<Integer,ErrorType> pair = foo.computeSomeValue();
if (checkErrorConditionOK(pair.getSecond())) return pair.getFirst();
else throw new AppropriatelyNamedExceptionTypeException(pair.getSecond().getErrorMessage());
// ...

This is pretty ugly. But foo has returned a couple values to us. And with Java generics, we even get some compile-time type checking -- pair.getFirst() is an Integer, and pair.getSecond() is an ErrorType. But that's a pretty ideal case with a nice descriptive type parameter. What if our parameters are of the same type?

// ...
TuplePair<Integer,Integer> pair = foo.computeSomeValue();
if (checkErrorConditionOK(pair.getSecond())) return pair.getFirst();
else throw new AppropriatelyNamedExceptionTypeException("Got error code: " + pair.getSecond());
// ...

From this code, I have no way of knowing that the second item in the tuple is an error code. I can infer it from the way the code is written. But maybe whoever wrote this code got the parameters mixed up. If I'm reading through this code (debugging or doing a thorough code review), I now have to open up foo's implementation to see what actually gets returned in that tuple.

In this case, our TuplePair is barely better than an array.

Why the Anti-pattern Persists

It's easy to understand why I keep seeing this pattern in Java, though. There's a lot of boring boilerplate code that has to be written to work around this problem. To make the code more meaningful and readable, and to allow for easier refactoring in the future, you should assign a name to each thing you want to return. (No, you don't want to do that with a Map. That's an anti-pattern for another blog post.) The best way to do this is with a new class.

In Java, writing a new class is pretty heavyweight. If you want to use proper encapsulation, your new class might look like this:

public class ComputedValue
{
private int value;
private int errorCode;

public ComputedValue(int value, int errorCode)
{
this.value = value;
this.errorCode = errorCode;
}

public getValue() { return value; }
public getErrorCode() { return errorCode; }
}

Since ComputedValue is a public class, you have the option of making it a static inner class, or going the more common route and creating an entirely new file. But at least now our code is a bit more readable:

// ...
ComputedValue computed = foo.computeSomeValue();
if (checkErrorConditionOK(computed.getErrorCode()))

return computed.getValue();

else throw new AppropriatelyNamedExceptionTypeException("Got error code: " + computed.getErrorCode())
// ...

What Scala Offers

Given the above option, it’s not surprising that people keep creating the TuplePair class. Frankly, it’s a pain in the ass to write new Java classes for every case like this. The thinking is “Write TuplePair once and re-use it so we don’t have to do it again,” even though it leads to useless names like getFirst() and getSecond().

Scala helps solve this class of problems by making creating these types of classes dead simple:

class ComputedValue(val value: Int, val errorCode: Int)

This single line gives you everything that the above Java implementation did: A type with “value” and “errorCode” properties, with a constructor for easily creating new instances. And while you didn’t have to write any boilerplate code to provide encapsulation, it’s there, implemented automatically by the Scala compiler.

Classes in Scala are public by default, and they don’t have to be declared in separate files or as static inner classes. That makes the barrier to entry to writing better code a single line in a file you already have open. The Scala version of the above snippet becomes:

// ...
val computed = foo.computeSomeValue();
if (checkErrorConditionOK(computed.errorCode)) return computed.value;
else throw new AppropriatelyNamedExceptionTypeException("Got error code: " + computed.errorCode)
// ...

But Scala gives you even one more nice feature in this simple case. So far, we’ve only been looking at code that consumes a ComputedValue instance. But what about the code that creates it?

// Java:
public ComputedValue computeValue()
{
return new ComputedValue(0, 42);
}

Even with our new ComputedValue class, this code still has the same problem as our TuplePair! Someone reading this code can’t tell which is the value and which is the error code. To be sure, we would have to open up ComputedValue and look at its constructor parameters. But we’re still not sure if the developer knew the proper parameter order when he wrote that line of code. We can’t see his intention from the information in the call to the constructor. I use the following pattern to make this more clear in Java:

public ComputedValue computeValue()
{
final int value = 0;
final int errorCode = 42;
return new ComputedValue(value, errorCode);
}

The above at least makes my intentions clear. But to check whether my argument order matches the argument order expected by the constructor, you’ll have to look at ComputedValue’s implementation. How does Scala improve on this?

def computeValue() = new ComputedValue(value = 0, errorCode = 42)

Again, a single line of Scala replaces several lines of Java. The code here is not only more concise, but more explicit. The return type of computeValue() is inferred by the result of the method’s implementation on the right-hand side. And by using named arguments, we not only make our intention clear to the reader, but also to the compiler. Scala will check at compile-time that ComputedValue’s constructor takes “value” and “errorCode” parameters, and will even re-order our paramters for us if we got them in the wrong order.

These are some of the most basic features that Scala offers, and yet they’re the ones that come up in day-to-day work over and over again. By getting tedious boilerplate out of the way, Scala lets coders write better code without worrying about making more work for themselves.

Wednesday, January 26, 2011

How much wrong can you pack into a single sentence?

From a story FoxNews.com, shared by @Jeramey:
Web developers have tried to compensate for this problem by creating IPv6 -- a system that recognizes six-digit IP addresses rather than four-digit ones.
... What? There are so many things wrong with that sentence.
  • Internet protocols are not generally developed by "web developers".
  • IPv4 addresses contain more than 4 digits. A fact a 2 year old could figure out if she'd ever seen an IPv4 address. (255.255.255.255, anyone?)
  • If you're talking about binary digits (aka: bits) IPv4 has 32.
  • IPv6 has FOUR TIMES as many bits as IPv4 -- 128 bits in case you're a FoxNews reporter and can't do multiplication.
  • which means that IPv6 addresses have FAR more than 6 "digits" as seen by end users. For example, the address of one of my home servers is 2001:470:b9cc:beef:214:85ff:fe21:2b2e
  • But it doesn't matter anyway, because on the web you'll still type "www.google.com" or whatever.
If you're curious, go read the Wikipedia page on IPv6.


Friday, May 28, 2010

Installing Windows 7 in VirtualBox 3.2

This post is mostly in hopes that if other people have the same issue I describe below, they'll find the fix by googling much more easily than I did. Read on for the details.

Yesterday afternoon and this morning, I spent more time than I care to admit trying to install Windows 7 into a VirtualBox VM at work. On the first attempt, using a MSDN installer disk that a coworker lent me, I got an error which I initially attributed to a bad DVDROM: "The file or directory [...] is corrupt and unreadable"

However, after downloading an ISO image from MSDN, I saw the same error with it. I even found that by moving the popup error window, I could find another error window below it with the error code 0x80070570. So, more googling. Which turned up a lot of results regarding odd SATA drivers.

Finally I searched the VirtualBox site directly and found this post showing the same error I'd seen. In it, there is a link to another post which has a solution.

Apparently, my mistake was upgrading to the Oracle-branded VirtualBox v3.2 before attempting my install. It seems the SATA controller it provides to the windows installer isn't so great. If you remove it from your system configuration, and instead attach your virtual hard drive to the IDE controller, your installation will go much more smoothly. By which I mean, actually work.

Monday, March 8, 2010

Glasses

Coworkers and friends pointed out to me a few times in the last couple of months that I couldn't see things that I ought to have been able to. Text on a television screen, the menu items written up on a wall, etc. So I went and got an eye exam this morning.

The last time I had an eye exam I think I was 14 or 15 years old. I went because my eyes were *constantly* dry, so much so that I carried around a bottle of eyedrops with me for months on end. I guess Mom or Dad thought it might've been some sort of eye problem so I got an eye exam. I distinctly remember during the exam that I was only asked to read the top few lines of the eye chart, even though I could clearly read every line on the thing. Sure enough, it turned out my vision was fine, I just had dry eyes.

Fast forward 15 years, and I'm sitting in an exam room with the eye chart on the opposite wall and I realize that even the top most line is a bit blurry. And there's no way I'm reading anything below the second line. Funny how it took looking at an eye chart (even before the doctor came in) to make that painfully obvious.

So in comes the doctor and repeats the same procedure as my last eye exam. "Which is better? One? Or Two? One? .... or Two?" Only this time my vision actually gets better. Having never worn glasses, it's really odd to just put something in front of your eyes and suddenly see detail that wasn't there a second ago. It's like life is suddenly in HD. (again)

After the exam I was walked next door to peruse frames. They sat me down at a desk and brought me different frames to try on, which seemed rather inefficient -- I could've walked around the place and tried on the frames that they had on display myself, but instead the sales guy kept popping back into the back to get some more. I asked if he had any frameless glasses to try on. I can't remember his exact reply, but it was something along the lines of "You don't want those, you're not old. We can do better." Ok, whatever. I ended up with some rectangular jobbers trimmed in a thin charcoal-colored metallic frame. Still not sure whether I actually like them, but they don't have to be stylin' if I'm mostly going to wear them for driving.

Thursday, March 4, 2010

Weird Java SAX XML Parsing Error

One of our customers reported some erroneous data output by our command-line client. After about a day's worth of tracing code, I traced the bad data to the default implementation of SAXParser in Sun's Java 6 JRE.

We use Apache XML-RPC as an XMLRPC client, and it just calls out to SAXParserFactory.newInstance() to grab a SAX implementation. Java 5 and 6 both offer default implementations, but the error only appears in Java 6 JREs. In my debugging, I can see the JRE6 implementation passing in obviously bad data to the ContentHandler.characters() method in the Apache XMLRPC implementation.

Thankfully, if you drop in a replacement SAX parser, Sun's implementation will call out to it, so fixing the error was as easy as dropping in Apache Xerces.

The thing is, I'd like to track down the issue so I can make a proper bug report. I haven't been able to find any similar bug reports via googling or directly searching Sun's bug database.

The bad data is coming from "com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser", so it looks like it's a(n older) version of Xerces, but after about 15 minutes of searching I still hadn't found the correct version of source code to attach to my debugger. If anyone has suggestions let me know, but otherwise I think I'm going to give up searching for the root cause of the issue.

Thursday, February 4, 2010

The Windows 7 Honeymoon is Over

About a month ago I finally bit the bullet and upgraded to Windows 7 from my aging Windows XP. It was about time to do a fresh install of Windows to clean up the cruft, and I had heard somewhat positive things about it from friends. And by "somewhat positive things" I mean things along the lines of "It doesn't suck nearly as much as Windows Vista did!"

I was happy with Windows 7 for a few weeks, but I've started to see some odd DNS issues. DNS resolution seems to be really slow, and often fails. I'd say about 1-2 out of 5 web page requests fail to load due to DNS resolution failure. Then I click reload once or twice, and suddenly it gets the IP address and works.

I first mistakenly attributed it to Time Warner's DNS servers which aren't always the best. But trying GoogleDNS and OpenDNS didn't seem to alleviate the problem.

Tonight I came home and putzed around on my (OS X) laptop for a while. I loaded a number web sites without issue. But as soon as I hopped onto my Windows 7 workstation the DNS issues were back. It seems to *only* affect this machine.

Apparently many other people are having similar issues when using a wide variety of hardware. I've now spent hours trying to debug the issue and am no closer to a solution.

It's not the network layer -- I can pingflood other machines on my network and not lose a single packet. I tried disabling all network protocols but IPv4, and that doesn't seem to change anything. I'm getting pretty close to wiping Windows 7 from my drive and reinstalling XP.

Sooo, if you were thinking of upgrading, maybe wait for another service pack or 3.

Monday, August 3, 2009

The Deal with the (non)Trip to Poland

Hello, Internet! Hello, my neglected blog! I'm writing this because I'm tired of re-typing the story about why I took off time to go to Poland on vacation... and the ended up not going.

A few months ago, I realized that the 94th "Universala Kongreso" was about to take place in Poland. I actually had money to go, and a couple weeks of extra vacation time just asking to be spent abroad, so I started planning my trip. Booked a flight, purchased a ticket to the conference, notified people I'd be there.

Then, a few weeks ago, after discussions with the roomie and some consideration of long-term goals, I decided that I'd really be happier moving back to Austin. The lease is up at the end of August, at which point I'll be taking a nice 3-day cross-country drive back to Texas. (Anybody looking for programmers!?) :)

At this point, I was a bit worried about going off on vacation right before leaving a job, but the tickets were already paid for and non-refundable, so I figured I might as well make use of them. I hadn't yet paid for hotel, travel within Poland (Bialystok is about a 3 hour bus ride from Warsaw) or food and other miscellaneous stuff. I was about 50/50 on whether I should go and have fun, or stay and save money. I decided on the prior.

So around 4:30 I show up to the terminal for my 6:15pm flight, LOT Polish Air #7 to Warsaw. I hang out for a while and board around 6:00. It was the end of a rainy day, so there were 40 planes in line waiting for takeoff. We get in line.

About an hour later, the plane is sweltering. People are standing in the aisles fanning themselves. The air conditioner clearly isn't working. The captain make an announcement and we leave the line for takeoff and return to the gate where mechanics are called to assess the situation. Meanwhile they open up the doors at the front and back of the plane to try to vent some of the heat.

Two hours later, we're still in the plane, but they think they've fixed the issue. They start re-packing the cargo hold. We enter the (now shorter) line for takeoff and (eventually) take flight for Poland.

About 15-20 minutes into the flight it's still pretty unpleasant in the cabin. I can hear them lower the flaps on the wings, and I feel us pitch forward to lose some altitude. I think to myself "great, we're going back." About 15 minutes later, the captain announces that, yes, we're going back to land at JFK.

Then we fly around for 2 more hours. I'm not sure why. They don't tell us. Maybe we were just waiting our turn to land. But when we did finally land around 1:00am, there were fire trucks lining the runway. I started to wonder if maybe they weren't telling us something. Or maybe it's just standard procedure to call out the firemen for unscheduled landings? But that sounds costly. (i.e.: unlikely.)

It takes another hour for them to get us off of the plane and get luggage unloaded/claimed. The airline arranges hotel rooms for those that aren't locals. They tell me that they'll reimburse me for my cab fare home. They tell us to "keep in touch" regarding flights.

I take a cab and get home around 3am, around the time that the flight should've been landing in Poland. I have a bite to eat, since the only thing they gave us for the 7 hours on the plane was water. At that point I've basically decided that I'm not getting on another plane that week. Then I pass out.

Over the next couple days schadenfreude spurs me to keep tabs on the flight. First, I search for information on how likely it is that I'll get a refund. Answer? Not likely. "Contrary to popular belief, airlines are not required to compensate passengers whose flights are delayed or canceled."

I call the airline to see if maybe they have a better policy than legally required. I learn that my flight hasn't been canceled, merely "delayed". The person I speak with has no more information than that. He gives me a number to call for more information. At 5:20pm (and later, at 7pm), the prerecorded message still says that at 3:00pm they will announce the new departure time for the plane.

The next day, I call again. Apparently the flight had taken off at 2am -- 36 hours later than scheduled. Oh well. I enjoyed my "staycation" and saved some money.

In short, if you want to fly to Poland, you may want to avoid LOT. The guy behind me was from Poland and said he flies LOT a lot (ha ha) because it's a direct flight, but he seemed completely unsurprised (though remarkably good-natured) about the technical problems. He had even claimed, the first time we returned to the gate, "They should just let us go. We're not flying anywhere tonight."