ruby picture

RCR 257: String#concat should be String#concat!

Submitted by Evan Miller (Wed May 26 16:05:32 UTC 2004)

Abstract

String#concat should be named String#concat!, and String#concat should become nondestructive.

Problem

irb(main):001:0> s = "foo" => "foo" irb(main):002:0> s.concat("bar") => "foobar" irb(main):003:0> s => "foobar"

Proposal

Let String#concat return a concatinated string, and let String#concat! take String#concat's current function.

Analysis

String#concat is destructive, and does not end in !. This does not follow ruby conventions, and is unexpected behavior.

Implementation

Trivial name change
ruby picture
Comments Current voting

This has come up before, and also with Array#delete and #delete_if and #concat. To paraphrase what Matz has said in the past: the idea of the ! names is to alert you to danger (usually but not always in the form of destructiveness) when it isn't already implicit in the name of the method. (Whether it's implicit in "concat" has also been debated :-) Look up "concat!" or "delete!" in the ruby-talk archives for more.

-- David Black


I see, and now I've done my homework, but I still disagree. Isn't it true that destructive functions are all as dangerous as any other? I certainly can't think of an exception to that rule.

If #concat should be destructive, then I'd expect #to_i to be also!

-- Evan Miller


First let me begin by saying that I don't like the use of the words 'dangerous' and 'destructive' when referring to bang (!) methods. A dangerous method is one that is not safe; in Ruby we disallow the use of potentially dangerous methods through the use of the $SAFE variable. We do use the word 'destructive' to refer to a method that modifies its receiver (see the FAQ), but I don't like this word either, because it only modifies the receiver; it does not destroy it.

Also, as pointed out in the FAQ, the bang is used to differentiate between two versions of a method; one that modifies its receiver and one that does not. In the case of String#concat, we do not have two such methods (or rather, we do, but the other method is called String#+, so there's no need to use two names that differ only by a bang).

-- Paul Brannan


Evan, to_i and concat are two very different types of method. The concept of to_i modifying the receiver has no meaning; you can generate an integer conversion of a string, but you can't actually turn a String into an Integer. You can, however,concatenate a new string right on to an old one. Strings can do that; they can't turn into Integers. So there's really no parallel there.

-- David Black


David,

I admit to being somewhat facetious by mentioning to_i, but only somewhat. I believe the conceptual example still stands. Consider String#center, Array#concat, Array#select, or any other method that returns what it takes if you must, the specific example doesn't matter. (I find your argument to be specious because Fixnum and Bignum lay precedent that it's OK for one Object to become another Object, but that's even further off topic and I'm not trying to address that here.)

The point is that all functions in general take a value and return a value, and it is not obvious whether a member is destructive or not without a ! marking or similar (As emperical evidence I direct you to the FAQ. Were it truly obvious, it would not have an entry)

-- Evan Miller


Paul,

While I understand your cavaets about the term "destructive", I'd like to point out that in the context of a variable modification is synonymous with destruction, as any new value replaces -- destroys -- the old one. Not all destruction ought carry a negative context.

As for the point about distincting two methods, I agree. That's why this RCR recommends adding a nondestructive #concat. Maybe I made a mistake in the scope of this RCR and I should extend it to add a recommendation that there be a nondestructive version of all destructive methods. To me this seems like a simple and obvious aspect of keeping the language consistant.

-- Evan Miller


This takes us full circle: Matz, I believe, considers "non-destructive concat" and "non-destructive delete" to be contradictions in terms. (At least that's what I gather from earlier discussions.) So the equivalent for strings of, say, sub/sub! is not concat/concat! but concat/+ . While this means that not every non-destructive/destructive pair follows exactly the same pattern, it is consistent in that it always takes into account the connotations of the words themselves.

-- David Black


It might be interesting to see how people might change their coding style if non-bang methods were automatically generated when a bang method is defined, e.g.:

  module AutoNonBangAdder
    def self.append_features(klass)
      def klass.method_added(m)
        if m.to_s[-1] == ?! then
          define_method(m.to_s[0..-2]) do |*args|
            return self.dup.__send__(m, *args)
          end
        end
      end
    end
  end

(if this were added to the interpreter then we could also not generate a method if one already exists, and not generate a warning if an automatically generated method is redefined; this would ensure backward compatibility).

-- Paul Brannan


I think << and + make good conceptual pairs, as do concat/concat!. I'm uncertain of the reasoning that pairs these functions up in any other way. I can't understand the application of this argument to disallow #concat on the basis of + without also disallowing #concat! on the basis of <<.

I take issue with the idea that an operation can implicitly carry a connotation that it will modify its receiver. I do not believe that it's possible for a single word to communicate that concept; the ! notification is required. Symbolic methods are unique since << can carry the same meaning of method! = or []= as destructive in any incarnation.

I'm not against method! carrying other meanings, such as for methods with dangerous side effects, and I'm uncertain of the application to more complex classes that carry many values, but I think that for simple classes such as Array or String it makes sense. Matz may believe that #concat must always modify, however again that assumption is not universal. I think that clarity is vital, and I haven't seen any reason here important enough to forgo clarity.

-- Evan Miller


  > Matz may believe that #concat must always modify, however again that assumption is
  > not universal.

Perhaps, but Matz is in charge of Ruby :-) Anyway, I'm just paraphrasing what I've seen (and you've presumably seen) in the ruby-talk archives; ultimately if Matz wants to reconsider he will.

-- David Black


Of course! I'm simply trying to persuade him. That's what RCRs are for, right? ;-)

-- Evan Miller


Strongly opposed 0
Opposed 2
Neutral 1
In favor 5
Strongly advocate 2
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .