Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Matthew Reinbold
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Matthew Reinbold ( @libel_vox )

Sometimes I'm Tempted To Use Try / Finally In The Worst Way

By on

Yesterday, I posted a tweet about how I am sometimes tempted to use a Try / Finally block as a way to get around creating an intermediary variable. I've never actually done this, as I think it's a bad idea. But, so help me, I have been many times tempted. And, while I still don't recommend it, I thought it would be interesting to see, from a technical standpoint.

Imagine a scenario in which I have a hash and I want to both delete a key from that hash and return the value stored at that key. In order to access the key, I have to perform the "get" before I perform the "delete," obviously. But, I also want to return the value; so, I have to store the value in a temporary variable so that I can return the value even after I perform the delete.

Something about this little dance just gets under my skin. I'm not sure why exactly - it's just one of those things. So, sometimes, in my dark moments, I'm tempted to side-step the intermediary variable by bastardizing the "finally" mechanism in a Try block. To demonstrate that this works, I'm going to "extract" two keys from a cache and log them to the console:

<!doctype html>
<html>
<head>
	<meta charset="utf-8" />

	<title>
		Sometimes I'm Tempted To Use Try / Finally In The Worst Way
	</title>
</head>
<body>

	<script type="text/javascript">

		var cache = {
			foo: "bar",
			hello: "world",
			fizz: "buzz"
		};

		// The extracItem() will pull the item out of the cache (ie, delete it), and
		// then return it.
		console.log( "Extracted:", extractItem( "foo" ) );
		console.log( "Extracted:", extractItem( "fizz" ) );
		console.log( cache );


		// I remove the given key from the cache and return the value.
		function extractItem( key ) {

			// WARNING: Never do this (for a variety of reasons). This is just something
			// that I am occasionally ** TEMPTED ** to do when I get super frustrated at
			// the fact that I have to create an intermediary variable to hold the value
			// before I delete the key from the hash. More than anything, this is just to
			// demonstrate that the concept works... NOT that it is good.
			try {

				return( cache[ key ] );

			// After we've returned the value, delete the key.
			} finally {

				delete( cache[ key ] );

			}

		}

	</script>

</body>
</html>

As you can see, the extractItem() method executes the return() and then the delete(). This actually reads quite nicely, in terms of an order of operations (which is probably why it's such a temptress). And, when we run the code, you can see that the correct value is returned and the correct keys are deleted:

Extracted: bar
Extracted: buzz
Object { hello="world"}

It works, but here's why I'll never do this:

  • It falls under the "too clever" category; it's dark magic.
  • The expression of the code is not aligned with the intent of the code.

NOTE: There is probably a performance overhead as well; but, it's so marginal that I won't even consider it as a negative.

Anyway, just needed to get that off my chest. In a perfect world, the "delete" action would just return the value and then I could sleep soundly at night.

Want to use code from this post? Check out the license.

Reader Comments

28 Comments

So you're not happy with the delete operator. Why not create an abstraction that has the semantics you expect?

console.log('set', cache.set('foo', 'bar'));
console.log('get', cache.get('foo'));
console.log('delete', cache.delete('foo'));
console.log('delete-again', cache.delete('foo'));

Result:
set bar
get bar
delete bar
delete-again undefined

FWIW, deleting a key and returning the (previous) value at the same time violates command-query separation.

https://en.wikipedia.org/wiki/Command%E2%80%93query_separation

15,640 Comments

@Patrick,

In the code that actually triggered this thought last night, I actually took that kind of approach. I had methods for .setItem(), .getItem(), and deleteItem() that all work like you would expect. But then, I created a subsequent method called .extractItem(), which encapsulated the intermediary-variable:

function extractItem( key ) {
. . . . var value = getItem( key );
. . . . deleteItem( key );
. . . . return( value );
}

That said, I am not sure that it necessarily violates Command/Query separation. I mean, in the purest sense, I guess so. But, the intent of the method is perform a specific mutation. As such, the "side effect" isn't really a side-effect, it's the thing you're trying to do. But, just a thought.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel