Skip to content

Instantly share code, notes, and snippets.

@austintaylor
Last active September 13, 2018 02:41
Show Gist options
  • Save austintaylor/5730467 to your computer and use it in GitHub Desktop.
Save austintaylor/5730467 to your computer and use it in GitHub Desktop.

So I had this situation where I needed to delete an object from the database, if it existed. This is what I wrote:

Ruby Option 1

obj = get_object(1)
obj.destroy if obj

And as I wrote this, it seemed a bit awkward to me. We always said Ruby had a really elegant syntax, but I guess I've come to the place where I find it a little grating. The thing about this example is the asymmetry of it. Conceptually I feel like the if check goes with the assignment, but it's separated from it in a strange way.

Ruby Option 2

if obj = get_object(1)
  obj.destroy
end

This seems better to me on the surface, but there are a few issues. In irb it warns that you're using = when you might have meant ==. This could potentially look like a typo to people reading the code as well. Another issue is that obj is still in scope after the end of the if when appears like it wouldn't be. Finally, and of course this is highly subjective, but I find the end keyward to be a little ungainly following a single line like this. I'll leave it to you to imagine the 3rd option with the assignment on its own line.

So I thought I would look at how you would do this in a couple of other languages I'm familiar with.

C

Object *obj = GetObject(1);
if (obj != NULL) {
  DestroyObject(obj);
}

So that's kind of the standard for how to structure this kind of code. I like the cadence and the explicitness. And of course, it's very traditional.

Javascript

var obj = getObject(1);
if (obj) {
  obj.destroy();
}

Basically the same as C with some scripting language sugar. Java would be halfway between C and Javascript.

Go

if obj := GetObject(1), obj != nil {
  obj.Destroy()
}

I think this one might actually be my favorite. The inline assignment is an intentional feature of the language, and doesn't pollute the containing scope. The nil check is explicit, which doesn't seem like a loss here.

Clojure

(when-let [obj (get-object 1)]
  (destroy-object obj))

So when-let is built in, and is designed for precisely this situation. It doesn't pollute the outer namespace, and it's all wrapped up with parentheses in a nice kind of way. (Actually if-let would be equivalent in this situation. The difference is how multiple statements are handled.)

Haskell

getObject 1 >>= lift destroyObject

As is often the case, Haskell is both the obvious winner and the obvious loser. Winner because, well, this is by far the shortest. There's virtually no syntax. And it's point-free, so we don't have to name obj anything. Beautiful.

The downside is that Haskell examples are always a little bit of a lie. This one assumes you wraped the result of getObject in a MaybeT. If you were using the more obvious IO (Maybe Object) you'd have to branch on the result.

do maybeObj <- getObject 1
  case maybeObj of
    Nothing  -> return ()
    Just obj -> deleteObject obj

And if your framework had some other monad stack, you would probably have slightly different code. (I mean I haven't worked in a real Haskell system, but that's my impression.) So the fact that this code is far more contextual than the other examples is a pretty big caveat in my opinion.

The other, obvious criticism is that this code is completely unreadable to the uninitiated, whereas all the other examples are pretty close to pseudocode, which does count for something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment