«« Check out José González The Agile White Box Tester »»
blog header image
Non-Fattening Syntactic Sugar: Ruby's unless

I have to tell you, while working on FanConcert with Ruby on Rails I've found Ruby to be a complete pleasure to work with. Only after using it for a few months do I understand all of the advantages of languages like Ruby that James Robertson has been blogging about. He uses Smalltalk but it seems very similar to Ruby (maybe I can find a comparison to link to). Honestly the best way is to get knee-deep into it, then you'll get it too.

First of all, when you're using a dynamically typed language I recommend you unit test. There's no compile-time type checking but your tests can do something even better: make sure all of the objects are behaving the way you expect them to. Then type-checking becomes unnecessary.

Ruby calls this "duck typing": if the object quacks like a duck, it must be one. If the object responds to a method call, it's quacking. Unit testing ensures your objects quack how you intend. The approach makes it easier to refactor Ruby code than Java, even when Java gets help from an IDE like Eclipse (refactoring Java code can be a search-and-replace nightmare without help from an IDE). You don't need to change declared types or method signatures with Ruby code, you just make the object quack differently and it will.

But let's get to something I really like about Ruby: the keyword unless, a negated if. Boy, does it make code more readable. Sure it's syntactic sugar but it improves the readability of if statements, which makes the code easier to understand, which makes the code easier to change, which makes the project more agile, which I really like!

The thing I like the most about unless is that it's bigger than that skinny exclamation point, which is used to negate boolean expressions. This alone makes code more readable to me. Here's a simple example that strips whitespace from a String object:

thing.strip! if !thing.nil?
thing.strip! unless thing.nil?

You can't call a method on a nil (null) object, so this is a common bit of code. The exclamation point on the end of strip! is part of the method name (meaning it is "destructive") so you can ignore it. Just look at how much more readable that second line is.

Another example is compound boolean statements, which are easy to mess up when you're trying to negate them. Remember:

not (A and B and C) == not A or not B or not C
not (A or B or C) == not A and not B and not C

That's just pseudocode and it's giving me a headache. All software and hardware people know that rule -- originally by De Morgan -- but it's easy to mess up in an if statement.

if !array.nil? and array.size > 0
if !(array.nil? or array.size == 0)
unless array.nil? or array.size == 0

I like the unless one for readability again. The syntax allows you to give the statements a different perspective when you're trying to figure out what they do: an if gives the conditions for entering the block while an unless gives the conditions for skipping the block.

Posted at November 22, 2005 at 03:07 PM EST
Last updated November 22, 2005 at 03:07 PM EST
Comments

As someone who uses VB.Net for my day job, I love the syntactic sugar. Or at least i've learned to accept it as part of the language, that really can make stuff more readable. Here's a nice one. In VB.Net 2005, they've added the IsNot Operator. you can use it in the following way.

if obj IsNot Nothing Then
'do stuff
end if

instead of

if not obj is nothing then
'do stuff
end if

As you can see, it makes it much more clear what the logic is in this case. Also, you'll notice that VB.Net uses words instead of symbols for the boolean operators. This makes it much more clear what the symbol means. Oh, and for comparing it uses =, and for assigning it uses =. It's context sensitive. So there's no...

While a = b.getnext()
'do stuff
end while

which is a little confusing, as it doesn't really make sense to test an assignment statement for true or false. Just because an object is assigned a null value, doesn't mean the assignment didn't happen, or that the result of the assignment was false. To do this in VB, you'd have to do.

a = b.getnext()
while not a is nothing
' do stuff
a = b.getnext()
end while

in this case, it's a lot more clear when it will exit the loop.

» Posted by: Kibbee at November 22, 2005 08:45 PM

I don't have much to add, but I use VB.NET at work as well. I like readability. I used to program in C and Assembly and boy was it hard going back to code you wrote months, heck weeks ago! There's nothing wrong in making things simpler to understand.

» Posted by: roy at November 23, 2005 12:08 AM

'unless' is one of those things Ruby took pretty much straight from Perl, just like 'until' (kind of like 'while not'). Definitely a bit of sugar that I don't mind.

» Posted by: Danny at November 23, 2005 06:15 AM

Glad you made the reference to Smalltalk - I view the common dynamic languages (SPR)(for Smalltalk, Python, Ruby) as a great way of communicatig ideas - many of use communicate great approaches as they approach problems in a similar way.

e.g. "unless" in lanaguages where everything is an object are quite trivial to implement. Based on your description of it in Ruby, in Smalltalk you could add this behavior like (to convert to ruby add dots where there are spaces ;-) :

[ thing trimBlanks ] unless: [ thing isNil ].

The first object in [ ]'s is a BlockClosure - its an object so simply add the method: #unless: to it. It takes a parameters a second BlockClosure. The code would look like this:

unless: aBlock
aBlock value ifTrue: [ self value ]


Such simplicity is what makes dynamic lanuages so much fun to use (as you point out).

Tim


in Smalltalk is

» Posted by: Tim at November 23, 2005 09:29 AM
Google
 
Search scope: Web ryanlowe.ca