Non-idiomatic method names in Ruby examples

Several learner-facing exercises require exclamaition-suffixed methods: issue_pass!, revoke_pass!, claim_free_popcorn!, update!, reverse! These are non-idiomatic uses of such names in the Ruby language, according to Matz:

The bang (!) does not mean “destructive” nor lack of it mean non destructive either. The bang sign means “the bang version is more dangerous than its non bang counterpart; handle with care”. Since Ruby has a lot of “destructive” methods, if bang signs follow your opinion, every Ruby program would be full of bangs, thus ugly.

Thus, in idiomatic Ruby, to suffix a method name with an exclamation (bang), there needs to be a corresponding method similarly named without an exclamation. However, no such corresponding method exists in these examples.

In exercism/ruby#1801, I propose we rename those methods to their non-bang forms across stubs, unit tests, and exemplar implementations (issue_pass, revoke_pass, claim_free_popcorn, update, reverse). The implementations themselves remain behaviourally identical; only their public signatures and matching expectations were adjusted to drop the non-idiomatic suffix.

Then, Ruby learners can work with idiomatic Ruby naming. Clarity is improved without altering the core exercises.

Hah. Interestingly this came up at work today where we discussed the difference between immutable and mutable and the non-bang and bang pairs in Ruby.

I agree with your overal assessment that: whilst there is no rule, the idiomatic thing to do is to only have bang variants if we have non-bang variants. Thus, your proposed PR makes sense.

Lets see what @kotp thinks about this.


:hourglass:Once maintainer consensus is reached here @orien , your PR will be re-opened if favourable, or remain closed if unfavourable.

Does this need to be updated as well? It implies bang methods are special because they mutate state unlike non-bang ones.

It is idiomatic, for sure, no question about that. This has the idiom of using the bang for a “dangerous” action, rather than for the “indicate that there is a non-dangerous version of the same name, without the bang”.

But dangerous can be communicated, and often is, even when there is no equivalent. For example, delete does not use a bang, but would inherently be considered “dangerous”. The idea of a delete and delete! would imply that the delete without is not (as) dangerous (perhaps it moves something to a holding area until it is really deleted after some time or later action?)

When we revoke a pass, for example in Amusement Park, this is a dangerous action, as we are revoking the pass that was issued, which should be considered with reasons. The “non-dangerous” version of that may not be apparent, but it is the issuing of the ticket itself. Even though it does not have the same name.

There are other issues regarding naming in Exercise as far as Ruby is concerned, when we use is_ , which I tend to talk about during mentoring, but leave it in the exercises, since it is a point worth talking about, especially for those coming from other languages.

For the claim_free_popcorn! bang, this is appropriate since it is itself a dangerous method that can crash you to the operating system, as it can raise an exception, not just let you claim a popcorn.

In the other method names that I have seen, I do not see where it is or would cause a problem.

Where the changes have been made in the pull request, each place appears to be dangerous on its own, even if the names are not necessarily the best they could possibly be, which is not really a goal here, as the names generally are “shared” from Problem Specifications.

The Linked List exercise, though. I think that is an appropriate change, if the linked list is not being modified in place. In Ruby, I would be surprised if I called reverse on a collection and it were modified in place, rather than having reverse! indicating that behavior.

The Gilded Rose exercise… well, this is a refactoring exercise, so I really do not think that it matters as much, as if they want to refactor to accommodate for the naming of the methods, that is within scope of the exercise, even if it is not mentioned directly.

I think you may have read too far into that statement for that implication.

“Many methods have equivalents which end in !”, so far so good, “which actually modify the string itself” attributing not the bang itself, but the method with the character as the last of its name, that modifies a string in place.

This is still true, and accurate, and hopefully does not suggest that it is some magical bang that causes this to happen.

1 Like

The original message has a quote by Matz saying that this is an incorrect interpretation of what bang methods were supposed to be.

The bang (!) does not mean “destructive” nor lack of it mean non destructive either. The bang sign means “the bang version is more dangerous than its non bang counterpart; handle with care”. Since Ruby has a lot of “destructive” methods, if bang signs follow your opinion, every Ruby program would be full of bangs, thus ugly.
- Matz

I am very aware of the quote, as it is in my teaching material and I think also in an interview we may have done directly with Matz around 2009.

2 Likes

That quote contradicts your earlier message in my opinion. Do you see it differently?

Looking at the larger picture, are bang methods discussed elsewhere in the syllabus? Renaming the bang methods in question means the students are less exposed to what seems to be a common language feature. In that sense, it would be useful to add a concept if it doesn’t already exist for bang methods. The concept can discuss the different perspectives on bangs in a more targeted way which helps the students.

The concept would be a separate discussion from these changes though.

1 Like

I don’t think having a separate concept for this makes sense. Since there is no de facto standard in using the “bang”. I would say it is a sub part of how to name methods and then I think the question if we should have a concept about how to name method should be a concept. I don’t really think it makes sense.

Lasagna feels way too early to do any justice to bang methods which is why I suggested a separate exercise. But I don’t want to sidetrack the main discussion further.

1 Like

I do, as when we were talking to Matz, the requirement to name all methods that are mutating things that are given, or the object the method is called on would be overkill. The idea is that they are there to be used when it makes sense to use them, rather than to make it a hard and fast syntax. A similar topic would be the query methods, having is_something rather than just having something?.

When the name itself indicates that it is dangerous, there is no need for the bang to indicate that, and in the case of having a delete and a delete! method, we might have the delete remove it as seen, but not really destroy it, we might store it in a bin somewhere until later enough time or an action actually deletes the information. But until there is a delete! method, then we would hopefully presume that the name delete itself is an indication that it would be something final.

With that clear, having the bang where they are currently makes sense to me, if we can recognize that a revocation of a pass would be the more dangerous (or alarming in this case, as to the reason for revocation is likely a security or health related thing in terms of the amusement park). For me, especially when the change happens in the exemplar example, the more fundamental thing would be to return false rather than nil in order to make it apparent by the pass itself that it was not only “not issued” (which results in a nil value) but instead revoked which should be a result that is not identical to not having been issued, and so likely false would be a better result for the exemplar code.

The point is, yes, I would not want to see bangs everwhere in Ruby, I would want to see them where when the name of the method itself indicates a danger or an awareness, but also, we should feel free to use them where they bring attention when we want it to.

That said, of course there is room for change, but I would hope that it is less “idiom” and more for “communicates the wrong thing”, which I do not find that they do, at least at this moment, for most of the made changes.

I am not saying that, for the Amusement Park exercise, that issue_pass! could not benefit from not having the exclamation mark come off of that name. The revoke_pass! may or may not, but it is, as per the tests, potentially the same as creating an instance of a pass but not issuing it. That is just one exercise example though.

I think that each exercise needs to be looked at individually, rather than with a goal of “remove the bangs as they are unidiomatic”, as it is a tool to be used for communication and no fixed rule is set, though opinions are there to be had.

2 Likes

I don’t agree that is_something vs something? is the same issue. Method names starting with is or has are not idiomatic and I’ll die on that hill.

I’ve always approached the bangs similarly to what you describe: if it’s dangerous, bang it. Rails has a differeng convention which bangs it to have it raise and non-bangs will add to an error object (don’t think that is necessarily a perfect way to determine dangerousness but okay).

However, i am ambivalent about “if its clear that its dangerous like delete”. I do not agree that it is necessarily clear that delete means dangerous especially not if there is a bang version. I do, in general, agree with how you approach that and I do agree that we shouldn’t necessarily say that there is a rule that bangness is only allowed if there is a non-bang equivalent. I think you elaborated well and expanding on the Matz quote.

As for the track: I am ambivalent about the current state of the methods given your elaboration. Do I think revoking or taking a ticket is dangerous? I don’t think so. Maybe for the person who owns the ticket, maybe for the object if there is no path to recovery. It becomes pretty subjective, but perhaps that’s okay…

1 Like

I agree with this in terms of Ruby, but in terms of Exercism, we get that from the Problem Specifications as other languages do not have the same luxury.

For the “is revoking a ticket dangerous” it may be a liability, but this is for the lawyers to argue for the amusement park, not us. ;) But the difference of having a pass be different when revoked than only never being issued a pass is a point of mentoring. That is not what this topic was introduced about though.

The documentation in Ruby even mentions what the bangs are for the core library of Ruby, and of course while Rails is Ruby, Ruby is not Rails, and their conventions are at their discretion, as our conventions for the libraries we have students create or maintain are at their discretion.

The exercises also do not stop the student from exploring the bang methods and creating non-dangerous variants, which is also a mentoring point.

If a mentor were to, or a student were to raise the question, they are free to explore the impact on the exercise and their learning.

If there is a bang version that communicates in the wrong way, I am all for that, and welcome to the discussion as well. But it is likely not as broad reaching as the current patch presents.

If the person that presents the ticket can narrow it down, perhaps there is a change to be done, but I would not think that Gilded Rose is the thing, perhaps Amusement Park or Amusement Park Improved (or both). I would love if the improved, the second exercise in the series, would use a version from the student from the first exercise. I do mentor that exercise, and will often have that as a literal continuation of that exercise.

For the linked list exercise, we probably would keep the bang if the object that is reversed is changed in place, and I would have to look closer at the exercise in full to see if there is room to do that (and if it is a practice or concept exercise).

There are things about some exercises that really irritate me when mentoring them, but I have also had the experience of when those things are “fixed” then the discussions for the students no longer happen and the ideas are not explored, and then they end up doing similar things where it really matters, when they should likely be exploring the if and how and why here in a learning environment. I try to make sure that the tests never stop us from doing the right thing, with those problems, though.

2 Likes

Hehehe, :nail_care:t4: lots of thoughts going through my head how this could go wrong :boom::laughing:

I think this makes most sense if there is a non-bang version (just if I compare it with the Ruby core library). It is generally a method I bang in my codebases as well. Perhaps that could be an addition, or perhaps an instruction could be added to start the discussion about a reverse bang and without and how that could/would/should look in that exercise?

I think this is good context too for why we may not want to change anything here.

1 Like

There are many methods in the Ruby standard library that mutate the receiver and do not include an exclamation mark in the name. For example, String#concat, Array#pop, Hash#clear.

These are idiomatic because they have no counterpart method.

As an education platform, I believe Exercism should provide idiomatic Ruby naming.

This makes a lot of sense to me. When the method naming relates to real-world or business logic naming, it should adequately communicate the seriousness of the operation.

However, I don’t believe simple low-level behaviours like receiver mutation or error raising are sufficient to require a method to be marked with an exclamation mark when a counterpart method does not exist.

1 Like

Let’s narrow the work in the PR to one exercise, then, if we can, so that we can focus on that, making the exercise better for these reasons.

There are examples of methods that have an exclamation in Ruby that does not have a corresponding “less dangerous” name, when it is destructive, for example Array#sort_by! does not have an equivalent Array#sort_by, though there is an Enumerable#sort_by. It is the one I can quickly think of, as I have had conversations like this over the decades that Ruby has been available.

In my example, of course, code does not happen in a vacuum and I am aware of the relationship between Enumerable and Array.

Linked List already deviates a bit from the Problem Specifications exercise, in that the Problem Specifications does not seem to focus on reverse at all. I was looking at Linked List, not Simple Linked List. Sorry about that.

I guess my question is this: Which exercises are you looking to work on in regard to this?

2 Likes

The obvious argument for those not having bang are the names themselves indicating the “danger”, not that there is not also a bang version. Without the bang version, it means that there is no “safer” version of these things.

They are idiomatic only because they are used, not because there is no counterpart method. By the definition of the word “idiomatic”.

There are quite a few methods with is_ and has_ and they are there for a reason, but it is not something I would encourage only for that reason, and would discourage for many more reasons. Yet they are idiomatic.

Let’s not focus on “idiomatic” as that word only means “a style used” and so anything is idiomatic. I get a lot of conversations because of my idiomatic code, but no argument that survives that my code is not idiomatic, as it is by definition.

Exercism is strongly influenced by the desire to cultivate fluency, instead. Not necessarily style.

1 Like

Thanks for indulging me and for all the effort you’ve put into this conversation. I appreciate it.

I’ll focus on just one occurrence. Although, I’m not sure I can change the PR with it closed.

1 Like