Thursday, February 28, 2008

The Myth of Duck Typing

Ruby aficionados swear by what they call "duck typing" and I figured, since I'd criticized C# in comparison with Ruby, it was only fair to return the favor.

Duck typing isn't new. Only the term is -- apparently coined in the last ten years, to describe one variant of dynamic typing. But the underlying principles have been around for a long time (it's Lisp's fiftieth anniversary this year).

But Ruby takes it to an extreme that is, to put it bluntly, unhealthy to quality code. The Pickaxe book (also known as Programming Ruby) has this to say (emphasis mine):

You'll have noticed that in Ruby we don't declare the types of variables or methods--everything is just some kind of object.
. . .
If you've come to Ruby from a language such as C# or Java, where you're used to giving all your variables and methods a type, you may feel that Ruby is just too sloppy to use to write "real "applications.

It isn't.
. . .
[O]nce you use Ruby for a while, you['ll] realize that dynamically typed variables actually add to your productivity in many ways. (p. 365)
and
If you want to write your programs using the duck typing philosophy, you really only need to remember one thing: an object's type is determined by what it can do, not by its class. (p. 370)
. . .
You don't need to check the type of the arguments. If they support [the method you're calling], everything will just work. If they don't, your method will throw an exception anyway.
. . .
Now sometimes you may want more than this style of laissez-faire programming. ... (p. 371)
and it goes on to explain that all you need to do is call respond_to? to see if an object responds to the method you're calling.

And therein lies the problem. I have seen Ruby code littered with calls to respond_to? Rails calls it about 200 times. To use Dave Thomas' term, it's sloppy.

Here's a typical use of respond_to?
def render(obj)
case
when obj.respond_to?(:to_html)
return obj.to_html
when obj.respond_to(:to_json)
return json_to_html(obj.to_json)
when obj.respond_to(:to_s)
return html_encode(obj.to_s)
else
# Not realistic, since all objects respond to to_s
raise "can't render"
end
end
Gee, wouldn't it be great if the language handled this automatically? What if we invented the idea of an interface, which you could use to declare a set of methods that you expect to be handled by an object? What if you could then specify that a variable or method parameter had to respond to the interface, with an appropriate type exception (uh, let's call it an interface exception) being thrown if the entire interface wasn't supported by the object? And what if we added the idea of method overloading, so that we could have alternate versions of methods that expect parameters that respond to different interfaces? And what if the compiler could check variables passed to methods to see if they support the requested interfaces and give you compile-time errors instead of runtime errors? Gee, C# does all of that.

None of this is fundamentally incompatible with Ruby. The advantages to dynamic typing that are mentioned are, in fact, advantages. It is hugely useful to not have to specify the type of an object when it doesn't matter. But there is certainly a problem with not being able to specify what you expect in an object when it does matter. And it does, way too often.

There is nothing in the concept of dynamic typing or duck typing that requires that interfaces (or even classes) cannot be specified. But, instead, Ruby pushes work onto programmers that the compiler and runtime system ought to be doing automatically. In Ruby, it seems to me that duck typing is dynamic typing with blinders on. It results in bloated, sloppy, repetitive code. Personally, I'd much rather see code like this:
def render(IConvertsToHTML obj)
return obj.to_html
end

def render(IConvertsToJSON arg)
return json_to_html(obj.to_json)
end

def render(IConvertsToString arg)
return html_encode(obj.to_s)
end
The appropriate method can be decided dynamically at runtime -- the decision is not made at compile time. But, once inside the methods, support for the specified interface could be guaranteed. Also, notice that there is no need for an extra method to handle the unrealistic error case that I tossed in to make this point, though we could certainly add any number of additional methods for additional things we want to convert. It would be not be hard to invent a syntax for easy specification of interfaces as well as on-the-fly specification of required methods for a variable declaration.

If the Ruby powers that be want it, Ruby can have all the advantages of dynamic typing and all the advantages of interfaces combined with great compiler support. What's the disadvantage?

Update 2/28: Changed example code.

Update 3/2: Follow-up posted.

Monday, February 25, 2008

What is Null?

C# got null wrong.

I really like C#. I think it's one of the best things that Microsoft has ever done. The .NET Framework is also very solid. And Visual Studio is a mature, solid development environment. As a group, they make application development much easier.

But C# got a number of things wrong and one of those things, in particular, has been bugging me recently. Every object in the system inherits from the Object class and that means that you can count on all objects supporting a minimum set of methods. The minimum set is Equals, GetHashCode, GetType, ReferenceEquals, and ToString. Unfortunately, null is not an object, so null doesn't respond to any of these methods. This is most obvious with

null.ToString();
which generates a runtime exception. Oddly,
if (null == null) ...
compiles and runs properly, but it shouldn't. Neither side of the comparison supports Equals. The compiler must special case this. There are other special cases as well. (string + null) and (null + string) both return the string.

But why doesn't null respond to ToString? The obvious answer is that it's not an object -- it's a special "magic" value. But magic values are almost never a good idea and I don't see why this case is an exception. The fact that null is a magic value is a compiler implementation detail which has nothing to do with how I want to use it as a programmer.

In recent languages, Ruby, which has plenty of flaws of its own, got this one right. Nil is a singleton instance of NilClass. But they also made a minor mistake. NilClass doesn't respond to the empty? method meaning that you can't use s.empty? unless you know for sure that the variable contains a string. Since Ruby's classes are open, I can fix this and I do (and don't worry -- there's plenty of time to talk about Ruby flaws in the future).

Can C# fix this? Well, there are some problems. If they just change it, any code that was written that relied upon the Exception being thrown will break. That wouldn't be good. But, it seems to me they could fix it by throwing the Exception anyway, then continuing on (and returning the empty string) if the Exception isn't caught. Since the uncaught exception would terminate the application, the worst that would happen is that some apps which would have crashed will keep going. They could do the same thing with the other Object methods as well.

Sunday, February 24, 2008

How To Do Support (Kudos to NetGear)

I was shocked today to receive a phone call from NetGear support (on Sunday, no less). It usually doesn't work that way.

Of course, it started out with a problem. My NetGear wireless router (the WNR854T) has been having problems. It's only six months old. About a month ago, when I switched it from WPA to WEP to accomodate an old laptop, it suddenly forgot the admin password. On a lark, I tried my WPA passphrase and it worked! The router had somehow swapped the router password for the inactive WPA passphrase. A few days ago, it stopped working completely. After spending way too much time on the phone with NetGear Support and resetting it three times, with various failure modes in the middle, I gave up and NetGear said they'd swap it out.

They had three options: (almost) free, if I ship it back to them first; $20, if they ship it to me first; and $30 if they ship it overnight to me first. I don't like much paying for support like this and all but the $30 option mean I'd be without the router for a week or more, so I said I'd call them back after I figured out how much of a hurry I was in. Then, I swapped the router with my wireless travel router, the Linksys WTR54GS (which I like very much, by the way). The only disadvantage to this swap is that the Linksys has a much more limited range on wireless. I can live with that for a week or two.

I was going to call them tomorrow, but they called me first. So, while it's really not a good thing that the hardware failed so quickly, kudos to them for trying to make things right.

Saturday, February 23, 2008

Merged feeds

As I've mentioned, I started three blogs at once. Yeah, crazy. Although a blog has a home on a web site (mine are on Blogger), a lot of readers use RSS or Atom feeds. For those readers who want to read all three blogs, I figured it would be useful to provide a single feed for all three blogs.

I was surprised to learn that Blogger doesn't provide this feature and that Feedburner and other feed packagers don't either. Fortunately, there's a solution. Yahoo! Pipes is "a powerful composition tool to aggregate, manipulate, and mashup content from around the web." That's a mouthful. But, in short, Pipes is a web service that lets you build a data manipulator, that takes input from one or more web services and produces its output as a new web service. Pipes supports a number of web services as inputs (though I've read some reports of problems with non-RSS inputs). But, the only one I need right now is feeds.

Although the complex features can be downright confusing and the user interface leaves a bit to be desired, it doesn't take much to build a pipe.

  1. Go to http://pipes.yahoo.com
  2. Log in with your Yahoo! ID (you'll have to create one if you don't have one yet).
  3. Click the big Create a Pipe button at the top.
  4. In the create a pipe window, drag in a Fetch Feed module from the Sources section and a Sort module from the Operators section.
  5. Type in the feed URLs, and configure the Sort operator as shown. Use the + icon to add additional feeds. Connect the modules to each other by clicking on the output circles and dragging to the input circles.
  6. Save
  7. You can test the pipe immediately using the Run Pipe... link at the top.
That's it. Now you have a working merged feed, with a URL that will look something like this:
http://pipes.yahoo.com/0BrqS5Tf3BGylqJG2h2EvQ
You can edit that so that it looks nicer, but that won't give you statistics. So, I recommend you head on over to Feedburner and burn it. Then, give that URL out to people.

The Yahoo! Pipes URL for this pipe is http://pipes.yahoo.com/this/mixture
The Feedburner URL is http://feeds.feedburner.com/thismix


Note: If you provide feeds from Feedburner or another feed packager, it's important to not use those feeds in your pipe as it will mess up your reader statistics. Use the original feeds from your blogging service. When you want to know your overall readership statistics, just add up the totals from the base feeds and the merged feed.

Bonus: Creating a Summary Feed

Some people use blog readers to keep up to date on what people are writing, but they like visiting the actual blog sites to read what interests them. To accommodate these people, I also created a Summary pipe which shortens the blog entries to about 250 characters. Here's what it looks like:
The Regular Expression operator in the middle does two things:
  • It truncates the description after 250 characters, always breaking at a space. The 500 character limit is large in case there is a long URL at the 250-character point.
  • If we truncated in the middle of a tag, remove it.
Update: I removed the links to my summary feed and I've deprecated it. It looks like everybody subscribed to the full feed rather than the summary (not too surprising, I suppose). I'm leaving the information here because it might still be useful to somebody.

Thursday, February 14, 2008

Why Fake Security is Worse than No Security

Most devs have heard the adage that fake security is worse than no security. By fake security, I mean those things that don't actually increase security but appear to. Asking for your mother's maiden name. The sign that says "Beware of Dog" when there isn't a dog. All the stuff we go through at the airport these days that isn't actually going to stop a terrorist (and if my bottle of water is so dangerous, why do they toss it in a big trash can and keep it in the airport?).

The usual response when I tell people this is, essentially, "don't bother me." How can fake security hurt? It can't possibly be worse than no security. There are three primary reasons:

  1. It gives users a false sense of security, causing them not to be alert to real potential security threats.
  2. Time spent on fake security diverts resources from real security.
  3. It can provide an avenue for a real security threat to emerge.
Here's a great example of #3:

My credit union, which I am otherwise quite happy with, had some fake security. The ATM is inside their lobby, which means there that both outer doors and inner doors. The inner doors are locked when the credit union is not open, but so were the inner doors. Notice I'm using the past tense here (more on that soon). To get into the lobby, you had to scan your card through a card reader. This supposedly ensured that only people with ATM cards could get in -- which limits it to, oh, almost everybody in the country.

But, it's worse than that. The reader was fake. It didn't actually read the card -- it sensed the presence of a magnetic stripe. So, any ATM card, credit card, supermarket card, etc., would work. The fake reader actually provided no security. Yet, it provided a sense of security.

I knew the reader was fake and I knew that it might be a security hole, so I always used a different card from my ATM card to open the door. On multiple occasions, I mentioned to people in the bank that the fake security was a joke, but, of course, I got the expected response.

Guess what? Here's what the credit union wrote:

At First Tech, the security of your accounts is a top priority. Recently, we learned that a card skimming device was illegally attached to the ... Branch ATM located at ..., sometime between December 8, 2007, and January 19, 2008.

"Skimming" occurs when fraudsters attach card reading devices to machines, such as ATMs, that can scan card information. Then, the fraudsters use cameras to capture PIN numbers. Using the skimmed information and captured PIN numbers, fraudsters produce a duplicate card to make purchases and withdrawals.
Reading between the lines, it looks like somebody replaced the outdoor reader with a skimmer. I think this is the case because:
  • The credit union has told me that I used the ATM while the skimmer was there and I think I would have noticed a skimmer attached to the ATM. And the door reader is the logical place to put it.
  • The skimmer was there for a long period of time, more than a month. I wouldn't be surprised if they didn't find it until after they started getting a lot of complaints from members.
  • I was not a victim, meaning that the fraudsters either failed to get my ATM card info or my PIN, or both. Had it been on the ATM, they would have gotten my card info and had a decent chance at my PIN.
  • And, last but not least, the fake security of the outdoor reader has unceremoniously vanished.
Did the fake security aid the security breach? The bank isn't saying, but it certainly seems likely to me. And, even if it didn't happen here, I hope this makes it clear how it could happen. The same thing can happen in software or in the airport.

By the way, the credit union recently posted some advice here.