10

For example, array.pop doesn't need a bang to permanently alter the array. Why is this so and what was the reasoning behind developing these certain Ruby methods without this conformity?

Nick McCurdy
  • 17,658
  • 5
  • 50
  • 82
Aaron
  • 6,466
  • 7
  • 37
  • 75

4 Answers4

14

Bang methods are most commonly used to distinguish between a dangerous and a safe version of the same method. Here are some example cases that one might want to distinguish with a bang/no-bang combination:

  • mutator methods - one version changes the object, the other one returns a copy and leaves the original object unchanged
  • when encountering an error, one version throws an exception while the other one only writes an error message to the log or does nothing

However, the convention is to leave the bang off if there is only one version that makes sense. For example, poping an array without actually changing it makes no sense. In this case, it would end up being a different operation: Array#last. A lot of methods change the object they are called on, for example setters. We don't need to write these with a bang either, because it's clear that they change the object.

Lastly, there are a few exceptions to this, where some developers might use a bang method without implementing a bang-less counterpart. In these cases, the bang is simply used as a way to making the method calls stand out visually. For example:

  • the method does something dangerous or destructive
  • the method does something unexpected
  • the method has a significant performance impact
Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168
  • 2
    In addition some frameworks extend conventions of using `!` to mean "does something you might not expect" - so methods that are designed to control flow by conditionally raising exceptions, or no-parameter persistent mutators (e.g. `model_object.activate!`). However, these are sometimes a single development team's idea for the convention, and not Ruby-wide. – Neil Slater Aug 13 '14 at 18:25
  • This is partially incorrect. While what you're saying about `pop` is accurate, the `!` suffix does not mean that something is a mutator, it means that it is dangerous. Refer to my answer and my example of `ActiveRecord::Base#save!`. – Nick McCurdy Aug 13 '14 at 18:26
  • @NeilSlater very useful remarks. Additionally, I have seen bang methods being used to denote that the method might have a significant performance impact. – Patrick Oscity Aug 13 '14 at 18:27
  • Thanks for your suggestions I have tried to improve my answer. – Patrick Oscity Aug 13 '14 at 18:41
2

The bang is used to distinguish between a dangerous and less dangerous version of the same method. There is only one pop method, so there is nothing to distinguish.

Note: the name of the method has absolutely nothing whatsoever to do with what it does. Whether a method is destructive or not depends on what code it executes, not what name it has.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
1

A suffix of ! means that a method is a dangerous version of another method. For example, save! is the dangerous version of save. Dangerous could mean editing in place, doing something with more strict errors, etc. It is not required to use the ! suffix on a method that is dangerous, but doesn't need a safer counterpart. Additionally, this is just a naming convention, so Ruby does not restrict what you can and can't do if a method does or doesn't end with !.

There is a common misconception that every method that edits something in place should end with !. This is not true, ! is only needed when there is a more dangerous version of a method that already exists, and this does not necessarily mean that the dangerous method edits in place. For example, in Rails, ActiveRecord::Base#save! is a version of ActiveRecord::Base#save that performs validations.

Nick McCurdy
  • 17,658
  • 5
  • 50
  • 82
1

The meaning of bang in Ruby is "caution". It means you should use the method with caution, nothing more. I cannot find the reference anymore, but people of authority said explicitly that bang ≠ destructive method. Bang is just a semantic element associated with caution. It is up to the programmer to weigh in everything and decide when to use bang.

For example, in my simulation gem, I use #step method to obtain the step size.

simulation.step #=> 0.42

and step! method to actually perform the simulation step.

simulation.step! #=> takes the simulation to the next time step

But as for #reset method, I decided that the word "reset" it's verbose enough and it is not necessary to use bang to warn the user that the simulation state will be destroyed:

simulation.reset #=> resets the simulation back to the initial state

P.S.: Now I remember, once upon a time, Matz said half jokingly that he regrets introducing methods with bang into Ruby at all, because bang is semantically so ambiguous.

Boris Stitnicky
  • 12,444
  • 5
  • 57
  • 74