Why are "Array#push" and "pop" not "push!" and "pop!"?

S

samppi

As a novice in Ruby, I love its elegance and consistence; it's now one
of my favorite languages. One of my favorite features is the practice
of ending the names of mutating methods with an exclamation point to
distinguish them from similar accessor methods, like "reverse" vs.
"reverse!"

I'm curious, though, about an apparent inconsistency with this rule in
Array: push, pop, shift, and unshift. All four methods modify objects
in-place, yet they don't have an "!". Why is this?

The only reason I can think of is that they lack "!"s in other
languages, which is a pretty tenuous reason at best--not many
languages name their methods "empty?" or "map!" instead of "is_empty"
or "map"...and in any case, Ruby is different enough in syntax and
style that it shouldn't even matter.
 
P

Pat Maddox

As a novice in Ruby, I love its elegance and consistence; it's now one
of my favorite languages. One of my favorite features is the practice
of ending the names of mutating methods with an exclamation point to
distinguish them from similar accessor methods, like "reverse" vs.
"reverse!"

I'm curious, though, about an apparent inconsistency with this rule in
Array: push, pop, shift, and unshift. All four methods modify objects
in-place, yet they don't have an "!". Why is this?

The only reason I can think of is that they lack "!"s in other
languages, which is a pretty tenuous reason at best--not many
languages name their methods "empty?" or "map!" instead of "is_empty"
or "map"...and in any case, Ruby is different enough in syntax and
style that it shouldn't even matter.

! doesn't signify a mutating method, it signifies a destructive
version of a method. Per your example, #reverse revers the array
without changing it, whereas #reverse! is destructive. #push and #pop
do not have a nondestructive version.

Pat
 
J

John Joyce

some languages and libraries include silly or useless things to
achieve symmetry and a peculiar consistency.
It's not always important or necessary.
Some things are just unique or different and don't need symmetry or
consistency or don't have it and no reason.
 
M

MonkeeSage


"Dangerous" seems kind of ambiguous. Does it mean that the code does
something other than I expect? Or, that it will break code relying on
the non-bang version? Or some combination? Or something else entirely?

E.g., people coming from Haskell / Clean would likely say that *any*
mutation of the receiver is "dangerous", and label everything that
doesn't operate on a copy with a "!"

a = [1,2]
b = a
def impure!(a)
a.replace([1,2,3])
end
impure!(a) # dangerous!
b # => [1, 2, 3]

Regards,
Jordan
 
C

Christopher Anderson

this is a comment I agree with, that I haven't been able to articulate
quite so well.

it's sometimes even a hindrance, as it places everything on such an
"even field" as to be impossible to sort through.
 
M

MonkeeSage


"Dangerous" seems kind of ambiguous. Does it mean that the code does
something other than I expect? Or, that it will break code relying on
the non-bang version? Or some combination? Or something else entirely?

E.g., people coming from Haskell / Clean would likely say that *any*
mutation of the receiver is "dangerous", and label everything that
doesn't operate on a copy with a "!"

a = [1,2]
b = a
def impure!(a)
a.replace([1,2,3])
end
impure!(a) # dangerous!
b # => [1, 2, 3]

Regards,
Jordan

In thinking about it a little bit and looking at the stdlib, I think
it is accurate to say that !-methods have one of these
characteristics:

1.) Modifies receiver in-place (e.g., String#succ!).
2.) Causes some side-effect that is incompatible or sufficiently
different from the non-bang version (e.g., Thread#kill!) -- i.e., is
"dangerous".
3.) Implies both 1 & 2 (e.g., String#gsub!).

So in regard to the question posed by the OP, it seems that yes, to be
perfectly consistent with the usual syntax, pop, push and un/shift
should be suffixed with a bang. However, like John said, sometimes it
just makes sense to break the syntax. And this is a perfect example;
those methods don't have any semantic meaning that does not fall under
the above characterization of !-methods (i.e., there would be no
"normal" version, just a bang version), and their names are perfectly
descriptive. Further, given the second characterization, people may
think they are somehow unusual from they way they work in other
languages (or just their "intuitive" sense). So there is really no
benefit to having them as !-methods.

Regards,
Jordan
 
J

John Joyce

To put it another way, consistency for consistency's sake alone is
not the goal.
Consistency helps make things somehow predictable, but sometimes it's
just bloat.

That said, if Ruby had immutable classes, it would be important to have
push # return a new Array object with the result of a push
push! # alter the Array object in place with the result of push
pop # return a new Array object with the result of a pop
pop! # alter the Array object in place with the result of pop
 
C

Christopher Anderson

so the next question is do you think those could be useful additions?
or, can the push/pop/shift/unshift in that case (as opposed to the
equivalent! term) be gotten in some other way that makes the current
way (no ! methods at all) most advantageous/efficient, since both ends
can currently be achieved.
 
J

John Joyce

so the next question is do you think those could be useful additions?
or, can the push/pop/shift/unshift in that case (as opposed to the
equivalent! term) be gotten in some other way that makes the current
way (no ! methods at all) most advantageous/efficient, since both ends
can currently be achieved.
Like I said, my top-posting pal, if Ruby had immutable classes...
I suppose it's possible to implement immutable Array and MutableArray
(we already have that one)
but how practical is it really?
I can understand wanting it to prevent objects getting stepped on
even accidentally, Cocoa does that a lot.
 
D

David A. Black

Hi --

"Dangerous" seems kind of ambiguous. Does it mean that the code does
something other than I expect? Or, that it will break code relying on
the non-bang version? Or some combination? Or something else entirely?

It varies. Just think of it as a "heads up". It means that you need to
make sure you really know what the method does.

A classic example is:

"abc".gsub!(/x/, "z")

which returns nil, because no substitutions have occurred. The ! warns
you that you really need to know what's going on -- in this case, both
that the receiver will be changed if there are substitutions, and that
the return value is different from that of the non-! version when
there aren't.
E.g., people coming from Haskell / Clean would likely say that *any*
mutation of the receiver is "dangerous", and label everything that
doesn't operate on a copy with a "!"

a = [1,2]
b = a
def impure!(a)
a.replace([1,2,3])
end
impure!(a) # dangerous!
b # => [1, 2, 3]

Well, people coming from Ruby wouldn't do that :) The thing to
remember is that ! methods always exist (or should always exist) in a
pair with non-! counterparts. The ! is not just an exclamation of
surprise; it's supposed to mean something *in relation to* its
context.


David
 
D

David A. Black

Hi --

To put it another way, consistency for consistency's sake alone is not the
goal.
Consistency helps make things somehow predictable, but sometimes it's just
bloat.

That said, if Ruby had immutable classes, it would be important to have
push # return a new Array object with the result of a push
push! # alter the Array object in place with the result of push
pop # return a new Array object with the result of a pop
pop! # alter the Array object in place with the result of pop

I think the whole question of what the best names were for those
methods would have to be revisited. Since push and pop are, by
convention and tradition, object-mutating methods, it would be very
weird to suddenly have "non-dangerous" versions of them that didn't
change the object, and to define "danger" as behaving the way push and
pop are always (as far as I know) understood to behave.


David
 
B

Bill Kelly

From: "David A. Black said:
The thing to remember is that ! methods always exist (or should
always exist) in a pair with non-! counterparts.

Hmm. That makes sense to me in general, but I wrote a ! method
recently that has no non-! counterpart. A non-! counterpart
wouldn't make sense in this case, and yet I feel the ! is reminding
me something special is going on with this method.

The particular case is a UI client library whose widgets are
hosted on a remote window server. The client is able to create
and initialize widget objects locally, without yet having
communicated with the remote server. Only when the instantiate!
method is finally called, do we go over the wire and instantiate
these widgets in the window server.

There is no non-! counterpart, but I feel the ! is very
appropriate for this method.


Regards,

Bill
 
D

David A. Black

Hi --

Hmm. That makes sense to me in general, but I wrote a ! method
recently that has no non-! counterpart. A non-! counterpart
wouldn't make sense in this case, and yet I feel the ! is reminding
me something special is going on with this method.

The particular case is a UI client library whose widgets are
hosted on a remote window server. The client is able to create
and initialize widget objects locally, without yet having
communicated with the remote server. Only when the instantiate!
method is finally called, do we go over the wire and instantiate
these widgets in the window server.

There is no non-! counterpart, but I feel the ! is very
appropriate for this method.

My immediate thought, as a user of the method, would be: why? :) If
it's not part of a pair, then the ! is usually a sign that it should
be named something else -- something where the name itself tells you
about what's happening (like push and pop).

I don't mean to sound like the ! police. It's just that I've never
seen a usage pattern that, in my opinion, came close to being as
useful and elegant as the one Matz employs and recommends. The
problem, basically, is that the ! itself has no meaning. It's not even
as meaningful as ?, which fairly clearly connotes a boolean. So the !
is entirely what we make of it, and I haven't seen any improvements to
the approach Matz takes among the variants of usage. As a general,
contextless warning sign, it's hopelessly unclear and impossible to
interpret. In !/non-! pairs, it starts to be able to say something.

The only standalone ! methods I can recall seeing that made sense to me
are the ones in Builder, by Jim Weirich, where the ! is a kind of
escape from having the method name be automatically used as the name
of an XML element. Come to think of it, those aren't really
standalone, since the equivalent non-! methods do exist.


David
 
J

John Joyce

Hi --



I think the whole question of what the best names were for those
methods would have to be revisited. Since push and pop are, by
convention and tradition, object-mutating methods, it would be very
weird to suddenly have "non-dangerous" versions of them that didn't
change the object, and to define "danger" as behaving the way push and
pop are always (as far as I know) understood to behave.


David
I understand where you're coming from completely with the tradition
and convention, but sometimes tradition could be broken.
I was just being hypothetical, after playing with Cocoa and Objective-
C lately, where most container classes come in mutable and immutable
varieties, methods there to work on immutable containers have no
choice but to return a new container. (where the new one can be
either mutable or immutable, since creation uses the same shared
initializer)
Just to illustrate the point if there were a purpose for ! with push
and pop.
Since we don't have immutable containers in Ruby, there's not a point
to these methods having a bang.
They inherently bang.
Just a hypothetical.
Since Ruby is Ruby, the flavor is distinctly mostly mutable
objects... yummy!
 
D

David A. Black

Hi --

I understand where you're coming from completely with the tradition and
convention, but sometimes tradition could be broken.

Yes, of course the parser will let you do:

def greet!
puts "Hi"
end

def valid?
@x = 3
end

but why would you want to? :) The point is that in this case there is
*only* tradition and convention -- meaning, Matz added these !-methods
to the language *so that* there could be dangerous/non-dangerous
method pairs. Any rededication of ! to another purpose (like, a vague
stylistic flourish meaning "What a strange method!" or a more general
"This changes its receiver" signal) just means that the whole thing
becomes method-name soup and has *no* meaning.

So it's not pure stodginess on my part when I say: let's let the ! do
what it's supposed to do. If we don't, then we've lost what I think is
an ingenious and extraordinarily expressive technique.
I was just being hypothetical, after playing with Cocoa and Objective-C
lately, where most container classes come in mutable and immutable varieties,
methods there to work on immutable containers have no choice but to return a
new container. (where the new one can be either mutable or immutable, since
creation uses the same shared initializer)
Just to illustrate the point if there were a purpose for ! with push and pop.
Since we don't have immutable containers in Ruby, there's not a point to
these methods having a bang.
They inherently bang.

They inherently change their receivers, but that's a separate matter
from the !.
Just a hypothetical.
Since Ruby is Ruby, the flavor is distinctly mostly mutable objects... yummy!

And, one hopes, mostly non-dangerous operations :) which is why it's
nice to have the ! to signal those "evil twin" methods.


David
 
M

MonkeeSage

They inherently change their receivers, but that's a separate matter
from the !.

Well...99% of the time it is, except when the ! only serves to
indicate that it operates in-place (String#reverse!, Hash#update!, and
probably a few others I can't think of offhand*). That's why I
characterized the ! as "either-or-both" regarding in-place/dangerous.
Even if it's not a good idea or breaks with the common usage, it is
used that way at least a few times in stdlib.

* Oh yeah, and I just remembered #power! (on Bignum I think) which
isn't dangerous and doesn't modify the receiver...it just raises to a
power. Where the heck did the bang come from?!

Regards,
Jordan
 
M

MonkeeSage

Well...99% of the time it is, except when the ! only serves to
indicate that it operates in-place (String#reverse!, Hash#update!, and
probably a few others I can't think of offhand*). That's why I
characterized the ! as "either-or-both" regarding in-place/dangerous.
Even if it's not a good idea or breaks with the common usage, it is
used that way at least a few times in stdlib.

* Oh yeah, and I just remembered #power! (on Bignum I think) which
isn't dangerous and doesn't modify the receiver...it just raises to a
power. Where the heck did the bang come from?!

Regards,
Jordan

Few more methods where the bang only indicates in-place operation:

String#succ!, Array#reverse!, Array#collect!, Array#sort!

Regards,
Jordan
 
D

David A. Black

Hi --

Well...99% of the time it is, except when the ! only serves to
indicate that it operates in-place (String#reverse!, Hash#update!, and
probably a few others I can't think of offhand*). That's why I
characterized the ! as "either-or-both" regarding in-place/dangerous.

I'm not sure I follow, so this may not be relevant, but in-place
changes (changes to the receiver) are one form of "danger", in this
sense. They're just not the only form.
Even if it's not a good idea or breaks with the common usage, it is
used that way at least a few times in stdlib.

I don't know of any that don't come in pairs, where the ! one
indicates "danger" (reverse/reverse!, exit/exit!, etc.).
* Oh yeah, and I just remembered #power! (on Bignum I think) which
isn't dangerous and doesn't modify the receiver...it just raises to a
power. Where the heck did the bang come from?!

Good question. I see this in ri but I don't see it actually on any
objects. I'm not sure what it is.


David
 
D

David A. Black

Hi --

Few more methods where the bang only indicates in-place operation:

String#succ!, Array#reverse!, Array#collect!, Array#sort!

In-place modification is, I think, definitely the most common form of
!-style "danger". It's not the only form, though.


David
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top