Feature #5185
closedSet#merge acts in place but Hash#merge does not
Added by trans (Thomas Sawyer) over 13 years ago. Updated about 12 years ago.
Description
Waste of brain cells to have to learn and recall they are different. The expected method would be Set#merge!, Set#merge would return a new Set instance.
OTOH, why not Set#concat ? Or conversely, Array#merge ?
Updated by tokland (Arnau Sanchez) almost 13 years ago
+1, I just got bitten by this. Hash#merge returns a new object, so one should expect Set#merge to behave the same way (Principle of Least Surprise).
Set#merge!, Set#update or Set#union! for in-places unions sound good to me.
Updated by mame (Yusuke Endoh) over 12 years ago
- Status changed from Open to Assigned
- Assignee set to knu (Akinori MUSHA)
I understand the problem, but I guess it is too late to fix it.
Anyway, I'm assigning this to knu, the maintainer of lib/set.rb.
--
Yusuke Endoh mame@tsg.ne.jp
Updated by knu (Akinori MUSHA) over 12 years ago
I feel it's hard to change this by now since #merge is a library method (for subclasses) rather than just a user method, but I could add #update as an alias for #merge and then obsolete #merge which would eventually be end-of-life'd.
Updated by trans (Thomas Sawyer) over 12 years ago
Adding #update is a good idea.
I would also add a warning to #merge stating that it's behaviour will change in a future version and to use #update instead. Then after a little while obsolete #merge altogether, but only for a bit, then bring it back with expected behaviour of producing new Set.
I advocate a slightly accelerated time scale for this transition b/c if some people are making the mistake of assuming Set#merge works like Hash's already and finding out the hard way that it's not, then I think that adds some impetus to taking the fast track.
Updated by alexeymuranov (Alexey Muranov) over 12 years ago
=begin
As the topic is surprising behavior of (({Set})) methods, i propose to deprecate (({Set#+})) as an alias of (({Set#|})), and maybe use it later for the symmetric difference. (I think symbols like (({+})) and (({|})) are too precious to alias one as another.:) )
Currently (({Set#+})) is one of a few (if not the only) uses of (({#+})) for an operation which is not injective in each of the arguments: for sets (({a})), (({b})), (({c})), the equality
a + b == a + c
does not currently imply
b == c
I would have also suggested (({Set#|=})) and an alias (({Set#reverse_merge})).
I also think such methods would be natural for (({Hash})).
I can open a new issue for this if there is some interest.
P.S. I think that (({Set#+})) as the symmetric difference would look particularly good with sets of integers or symbols.
=end
Updated by knu (Akinori MUSHA) about 12 years ago
- Status changed from Assigned to Rejected
On second thought recalling my original intention, I would say #update doesn't really fit for sets.
The word "update" indicates that some data may be lost through an operation by overwriting, but Set#merge does not cause any data loss (in terms of the equality definition in Set) whereas Hash#update does. That's why I did not name it "update".
As for Set#merge, I admit it wasn't the best choice when we had Hash#merge, but you can always use the "|" operator to avoid confusion. I'd also point out that there are not many examples where #merge is not destructive. In fact, the majority works destuctively. (simple grepping in ruby's source tree and some other gems showed that)
I don't like Set#concat because the word concat[enate] usually means appending something at the bottom but Set has no sense of order.
I don't like Set#union! either because the word "union" is a noun, not a verb that a bang method is usually derived from.
Updated by trans (Thomas Sawyer) about 12 years ago
Well, maybe #merge wasn't the best choice for Hash in the first place. Who knows. I only know that polymorphism is so incredibly useful in OOP, that it's a chink in the armor for the language when inconsistencies of this nature remain.