Ruby’s Redundant Exclamation Marks

When I first read (in Why’s (Poignant) Guide to Ruby) about Ruby having ‘destructive’ methods, I couldn’t think why such a redundancy would be necessary in a language. Destructive methods are those ending with an exclamation mark; they are called destructive because they may change the instance its operating on. Take for example the String#gsub method. In its non-destructive form it returns the substitution you ask it to perform:

irb(main):001:0> s = "abcde"
=> "abcde"
irb(main):002:0> s.gsub(/bcd/, 'thlet')
=> "athlete"
irb(main):003:0> s
=> "abcde"

As you can see, the object s remains unchanged after the gsub. If you do the same thing with the destructive variant, s will be changed:

irb(main):003:0> s
=> "abcde"
irb(main):004:0> s.gsub!(/bcd/, 'thlet')
=> "athlete"
irb(main):005:0> s
=> "athlete"

Redundant, because you could also have used the non-destructive gsub and assigned its result back to s. Sure, it’s shorter; but only by two characters in the source code; and I don’t believe it’s a DRY violation to write s = s.gsub(...).
Since then, however, I have come to believe there may be good uses of the destructive method construct. For example when you’re writing a business method that needs to alter the object it’s operating on. In that case, using an exclamation mark notation will alert you about the method’s ‘destructive’ behaviour.

My friendly Ruby guru took an altogether different approach to all this, when he wanted destructive variants of methods he’d already written. Obviously, he didn’t want to duplicate the code; but he also didn’t want to write a wrapper for every destructive variant. So he used meta programming to write a def_variants method that can be used to add a destructive variant, and — as a bonus — a class method with the same name. The gsub! method can now be written as def_variants :gsub. (Well, not quite, but you’d have to read the entire post. Which is, unfortunately, in Dutch. The code samples aren’t though, and if you send Remco an email he might consider an English translation.)
I’m still not sure though if his solution put an end to this redundancy, or introduced even more redundancy…

2006-01-22. 3 responses.

Comments

  1. […] Danny’s Blog Pondering Programming and Poetry « Ruby’s Redundant Exclamation Marks […]

  2. if your destructive method is very efficient at manipulating the object, you’ll use that first and base the non-destructive method on that. for example:

    def gsub(pattern, replace)
    str = dup
    str.gsub!(pattern, replace)
    str
    end

    if you think beyond strings, and to more generic objects, you can easily create non-destructive methods from destructive methods, by implementing dup. the other way around, you need to implement dup and replace.

  3. Thanks, Assaf. Remco explained the same thing to me today. I guess destructive methods are more than just redundant versions of their non-destructive variants.