Should the "Series" exercise be fixed or clarified?

Hey everyone,

Garry’s here, with my first post too!

I have been going through the Rust track and thoroughly enjoying it, but ran into a confusing problem in the “Series” exercise, which I would like to discuss further. As you’ve probably guessed, it is around expecting 6 zero-length sequences out of the 5 character number.

The initial motivation for this is explained in this comment on GitHub and is mentioning the principle of least surprise. I do think this principle is great and should be respected, but I’d like to argue that in this particular case it actually is far from the “least surprise”. It is getting into the obscure realm of division by zero maths - “how many zero-length sequences can I divide this string by?”, and is by no means logical or intuitive. If that would be some sort of library, and I had a bug where my prior calculations somehow resulted in the zero being passed in, it would be pretty hard for me to debug a problem like this. Besides, any other language tracks are throwing exceptions and raising errors in this case and I don’t really see why Rust has to be special.

What do you think? Should we actually change the exercise to make more sense? If not, why? Should we document this bizarre expectation explicitly? The “if you ask for a number of 6 character long sequences out of a 5 character long string, you get what you deserve” is explicitly documented, but there aren’t any mentions of the zero case.

Curious to see what everyone else thinks.

Cheers,
Garry

Thanks for posting about this.

I actually agree with you that the way it is right now is not intuitive. If this exercise was being implemented from scratch or very new, I would be in favor of doing it like other languages and returning an error.

However, changing it now breaks all existing solutions. Existing community solutions become invalid.

And at the end of the day, I don’t think this is important. I wouldn’t weigh the principle of least surprise very heavily here, because the tests remove any surprises very quickly.

Should we document this bizarre expectation explicitly?

It is my impression that on Exercism in general, the introduction text to an exercise often doesn’t mention every edge case. That’s what the tests are for, they are “executable documentation” of the precise specification of the exercise. Exercism users are expected and encouraged to do TDD by enabling one test case at a time. The test case in question is actually first one, so users become aware of this requirement very quickly.

Would you mind laying out, in simple English without referencing other pages, what test case is surprising and why?

I believe they are referring to this one:

fn with_zero_length() {
    let expected = vec!["".to_string(); 6];
    assert_eq!(series("92017", 0), expected);
}

For reference, here’s the Python version, which expects an exception:

def test_slice_length_cannot_be_zero(self):
     with self.assertRaises(ValueError) as err:
         slices("12345", 0)
     self.assertEqual(type(err.exception), ValueError)
     self.assertEqual(err.exception.args[0], "slice length cannot be zero")

I’m a simple person who needs things in simple English terms. So … the issue is the behavior when the function is asked for zero-length series from a string of n chars? Python triggers an error and Rush expects it to return n empty string?

Of those two behaviors, the Python approach seems more reasonable to me :slight_smile: Would it be instructive to compare this to what other languages do? Is the ask here for Rust to expect something similar to Python’s implementation?

the issue is the behavior when the function is asked for zero-length series from a string of n chars?

yes

Python triggers an error and Rush expects it to return n empty string?

yes

Of those two behaviors, the Python approach seems more reasonable to me

I agree!

Would it be instructive to compare this to what other languages do?

@SirGarry says most if not all other languages do it like Python. I believe them, because it seems like the more reasonable thing to do.

Is the ask here for Rust to expect something similar to Python’s implementation?

Yes, that’s my understanding. I’m currently not in favor because it’s a breaking change and I don’t think the exercise is much worse because of this one weird edge case.

If Rust does something different to the other tracks, then adding an .docs/instructions.append.md file is probably the best approach. This won’t break all existing solutions, but will remove confusion.

Docs: Practice Exercises | Exercism's Docs

3 Likes

PR.

Feedback on the wording is very welcome.

2 Likes

Thanks a lot everyone, both for the discussion, as well as a pretty decent solution we ended up with.