Showing posts with label weak typing. Show all posts
Showing posts with label weak typing. Show all posts

Tuesday, February 18, 2014

An alternative to function overloading

Method overloading has always struck me as a bit clunky. It separates a method’s main code from helper code, adds clutter, and doesn’t play nicely with inheritance (at least in Java). On the other hand, its ability to provide variants for a given method is useful. I think Effes provides a better alternative.

Overloads provide two axes by which you can create variants of a method: they let you omit arguments by supplying a reasonable default, and they let you pass in a value whose type is similar to (but different from) the “main” type. For instance, you can imagine a method add(double n, RoundingMode mode) with an overload add(long n). That second overload would call the first variant, casting the long to double and using RoundingMode.HALF_UP.

Lots of languages let you omit arguments by providing default values: add(n, mode=HALF_UP) or similar. Effes will, too, but it’s tough for a statically typed language to handle the arg-of-similar-type problem. The only thing you can really do is to accept a supertype, like add(Number n). But to do that, you need control over the type hierarchy, which you obviously may not have.

In Effes, you can use disjunctive types instead:

add(n: Double|Long):
  d = case n of
    Double: n
    Long: n toDouble -- e.g., if there's no automatic type promotion
  ...

Thursday, May 30, 2013

Weak typing is weak

I spent a couple hours the other day digging through layers of some unfamiliar code to track down a bug that ended up being a really simple, one-line fix. That's nothing spectacular, but this bug had to do with weak typing, which gives me a good opportunity to rant against that. Here was the fix, with some variable names changed to protect the innocent:

--- foo = bar[key] or null +++ foo = if bar[key]? then bar[key] else null

The idea was to get bar[key] if there is such an entry, or else default to null. I recognize that pattern because I've seen it before, and I've written similar code. It's a common pattern; the CoffeeScript page even uses it in an example. (In case you're not familiar with CoffeeScript/JavaScript, the original line works because a or b in CoffeeScript means "a if a is a true value, otherwise b." It's used as a quick get-or-default pattern.)

The problem arises if bar[key] is a defined, useful value that happens to also be a false value — like an integer 0, an empty string, or (to state the obvious), the boolean false. In that case, since the first expression is a false value, the whole expression evaluates to the second expression, null; the original line translated a 0 into a null.

In other words, the problem is that the first expression in a or b doesn't have to be a boolean, it just has to be interpretable as a boolean — and it just so happens that any type in JavaScript/CoffeeScript can be interpreted as a boolean, meaning that it's really easy to use the a-or-b-as-defaulter pattern, which happens to be wrong in many cases. And come to think of it, why are we using the same construct for boolean logic and getter-with-default, which are two completely different concepts?

Here's my issue with that: it's always valid code, and sometimes it's a valid pattern, but the language doesn't tell you when. And that, in a nutshell, is my beef with weak typing.

Now, there are lots of ways to write bugs in code, and it's reasonable to ask why I'm singling out this one class of bugs. I would argue that whereas other bugs are due to misrepresenting a problem, or to applying the wrong solution to a problem, this bug is due to applying the wrong dialect  of the correct solution. Ugh. Debugging is enough of a pain without the compiler encouraging us to write the programming equivalent of a tongue-twister. Weak typing makes it easy to write bugs, and, once you've written them, the type system does absolutely nothing to help you find them.

When it comes to programming, I want the computer to mean what I say. The quid pro quo is that I need to say what I mean. If what I mean is "bar[key] if it exists, otherwise null," then the language should require that I just say that. It's well and good for a language to make that pattern succinct, but blurring the lines between boolean logic and plain-old-values is funky reasoning that can lead to funky bugs.