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
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.:
(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
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