Skip to content

Add fizzy: an exercise teaching advanced generics and impl trait #828

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 13, 2019

Conversation

coriolinus
Copy link
Member

This will probably always be a track-specific exercise; I don't know of many other tracks for which "figuring out the right trait bounds" makes a good exercise. Still, I think it's a good idea to teach complicated techniques by doing a simple thing in a complicated way, hence this exercise.

I implemented my version because it was late and I was bored.
Then I realized I had something interesting, and turned it into
an exercise.

Closes exercism#821.
@coriolinus coriolinus requested a review from petertseng May 3, 2019 21:32
"difficulty": 7,
"topics": [
"generics",
"impl_trait",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a Rust-specific topic, but I think it's appropriate here.

#[ignore]
fn test_f64() {
// a tiny bit more complicated becuase range isn't natively implemented on floats
// NOTE: this test depends on a language feature introduced in Rust 1.34. If you
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These last two tests require std::iter::successors, a 1.34 feature. It would be possible to work around it and support an earlier compiler, at the cost of a more complex test implementation in each case.

I know of no track-wide policy on the minimum supported rustc version. We should decide between:

  • it is ok for an exercise to require use of the most recent compiler
  • it is ok for an exercise to require use of the most recent compiler if it includes an apologetic note (as here)
  • the track as a whole can require no feature newer than some specified compiler version

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference, at strength 1/10, is to allow the most recent stable compiler. The reason for this preference is in the service of keeping the code expressive (high signal to noise ratio), and that I personally am able to stay sufficiently up to date.

Useful clarifying point about the note: When 1.34 is sufficiently old, would the note be eligible for removal?

If one goal of the track is to optimise for accessibility, we may be required to only use some version that is widely available. For example, if a significant portion of students use distribution D and that distribution's package manager contains Rust at 1.32, then perhaps we would choose to stay at 1.32.

I do not know of the characteristics of the students, so I can't make decisions on that dimension. We may have to wait and see.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree: I also have no trouble keeping up with the most recent stable compiler, and therefore feel like it a reasonable thing to require of the students. We can revisit this if we start seeing student issues complaining that they don't have access to a recent stable compiler, but for now, let's decree the Official Track Policy: exercises may require use of the most recent compiler.

"uuid": "6b209749-d4af-45c2-bbdc-27603ce6979f",
"core": false,
"unlocked_by": "luhn",
"difficulty": 7,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note on positioning: the Rust track currently has three exercises other than fizzy with the "generics" topic. None of them are core exercises. The easiest of these is a difficulty 4, unlocked by "luhn", so I chose to make this a peer exercise.

I don't believe that it's worth worrying too much about positioning, because Rust is currently in the middle of the track reordering project, and we can expect the position of this exercise to shift, possibly substantially.

Copy link
Member

@petertseng petertseng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Approval says:

  • An inspection of the provided code and tests indicates that these look reasonable.
  • It seems to be a good idea to include this exercise.

What is missing from this Approval, but would have been good to have:

  • An attempt by me to solve this exercise from the student point of view: Remove and do not refer to the example solution, and try to make the tests pass by only starting from the provided stub. Give a report on the experience of doing so.

Since I did not have time to do that yet, I leave open the choice of whether to wait for me to do that (I can't guarantee any specific time. This weekend?????), wait for any other reviewer to do that, or to simply put it out there and let students give that feedback.

@@ -0,0 +1,3 @@
/target
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regarding this gitignore, you may want to match the other exercises' gitignores exactly, else you may get a PR like #592

"1", "2", "fizz", "4", "buzz", "fizz", "7", "8", "fizz", "buzz", "11", "fizz", "13",
"14", "fizzbuzz", "16",
];
// a tiny bit more complicated becuase range isn't natively implemented on floats
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// a tiny bit more complicated becuase range isn't natively implemented on floats
// a tiny bit more complicated because range isn't natively implemented on floats

#[ignore]
fn test_f64() {
// a tiny bit more complicated becuase range isn't natively implemented on floats
// NOTE: this test depends on a language feature introduced in Rust 1.34. If you
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference, at strength 1/10, is to allow the most recent stable compiler. The reason for this preference is in the service of keeping the code expressive (high signal to noise ratio), and that I personally am able to stay sufficiently up to date.

Useful clarifying point about the note: When 1.34 is sufficiently old, would the note be eligible for removal?

If one goal of the track is to optimise for accessibility, we may be required to only use some version that is widely available. For example, if a significant portion of students use distribution D and that distribution's package manager contains Rust at 1.32, then perhaps we would choose to stay at 1.32.

I do not know of the characteristics of the students, so I can't make decisions on that dimension. We may have to wait and see.

@coriolinus
Copy link
Member Author

Thanks! I'm happy to leave this for another week or two until you get the chance to attempt it from the student perspective. I estimate that a student will probably spend about 90 minutes implementing this: it took a little longer for me to write it the first time, but students have the API and tests provided for them, which should help speed things.

@petertseng
Copy link
Member

I have completed an implementation at https://github.com/petertseng/exercism-rust/blob/fiz/exercises/fizzy/src/lib.rs .

It had been sufficiently long since I reviewed this PR that I didn't remember any of the example at all, so I was doing things from scratch.

The 90 minute estimate was just about exactly where I landed, though I took about a 15 minute break away from the computer at the 45 min mark. A fair bit of my time was spent trying to figure out appropriate lifetimes. I didn't end up figuring out how to remove the 'static at line 10.

@petertseng
Copy link
Member

As to what I learned: I was a bit busy thinking about lifetimes to give a lot of thought as to what impl trait means. I saw the mention in the code comment so I knew to look for https://doc.rust-lang.org/edition-guide/rust-2018/trait-system/impl-trait-for-returning-complex-types-with-ease.html myself and look at it. Although I got it here, it may be difficult to extrapolate from this and know to use it in a situation where a stub isn't given. I'm not sure how I'd solve this problem though, since not providing the stub would mean potentially a lot of time spent figuring out the right type for apply. So I don't have a suggestion that I like better than keeping things as is.

@coriolinus
Copy link
Member Author

For the 'static in line 10, I think it's just necessary for the F parameter. I decided I was OK with this because I couldn't come up with a plausible reason for someone to attempt to use a function with a lifetime shorter than 'static. For the S parameter, I avoided 'static by storing subs in the Matcher object, which meant that it could have a shorter life than F.

I believe that we can summarize your experience implementing this exercise as "worth doing, no exercise changes required". As such, I will now merge this.

@coriolinus coriolinus merged commit da21df6 into exercism:master May 13, 2019
@coriolinus coriolinus deleted the advanced-generics branch May 13, 2019 06:24
@tintinthong
Copy link

In reference to the PR above, perhaps its worthwhile to add pull request to the problem-specs on including fizz-buzz

@coriolinus
Copy link
Member Author

coriolinus commented Jul 28, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants