-
Notifications
You must be signed in to change notification settings - Fork 541
Implement Space Age #177
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
Implement Space Age #177
Conversation
56c8ede
to
c6c5286
Compare
I wouldn't spend much time looking at the example code yet. It's not meant to be good. Just there to get the tests passing. |
I have observations, but not necessarily positions on how things should be done. I will give the obesrvations, and maybe come back in a day with positions when I have had time to think about them.
Currently, we would have to accept that the example's partial equivalence is not symmetric, which is contrary to https://doc.rust-lang.org/core/cmp/trait.PartialEq.html . We can see that if we changed one of the assertions from Although the symmetricity would not get tested unless we explicitly have assertions both ways in our tests. So I guess one question is "the tests don't require that the partial equivalence is symmetric; should they?" The same goes for transitivity if Huh. This gets quite verbose quite the more types we wish to define as equal to one another, since O(n^2) pairs of I wonder what it would be if the various |
Yeah, I had already decided that my Value Object idea was terrible. This is just more evidence to support that. I'll switch that part of the implementation. |
The value object approach was a bad idea. See exercism#177 (comment)
BUT YOU FORGOT PLUTO I am obviously joking, but I am (and I guess that you are also) old enough to have grown up with Pluto being a planet. I will attempt the exercise myself and see what I think of the idea of using this exercise for traits. The stub file is very verbose. I will attempt to curb the verbosity when I implement, but of course we can't guarantee that all students will think this way. Perhaps that has to be left to discussion on the site. If I can't find a way to curb the verbosity, then I would question the choice to make each |
Life would probably be better if there was a tolerance on the expected values
I don't think I should be required to round that |
the |
The least-repetitive I came up with: https://gist.github.com/petertseng/0b3928f0ee8881e94bf654d3f2153df9 Repeating It obviously fails the tests because it doesn't round, but it would ucceed if it did (or if the tests didn't care) Not necessarily an advocation for putting this in the example solution. Wonder what others can come up with to reduce duplication first. |
Macro rules. Interesting! Never occurred to me. I think my approach looks something like this: https://gist.github.com/9a9ddb50afd08f4ae5a20258c056916b My least favorite part of it is that the I will also have to implement Questions like that are why I structured the tests the way I did, honestly. I want to push students towards thinking about generics, traits and code reuse. To me that's how we make this problem interesting and valuable to people learning Rust. |
I only use them to take care of the duplication that I could not avoid.
There absolutely is. See the
You certainly got me interested and made me learn a few things, so you have me convinced. |
Over the course of several hours I worked on a long comment explaining why this approach wouldn't work. Turns out I rubber ducked myself I think this does what I want https://play.rust-lang.org/?gist=9e1acfeae7ebfdc7dd8176c1c015adb9&version=stable&backtrace=0 It's pretty different from the first approach. After working on this a few ways, I realized that at its core, the problem has a couple of concepts
The language of the exercise makes you think about "Years" as a property of a person, or some other object separate from a Planet. So I kept trying to put that logic somewhere outside of Planet. But a "year" is the same as a planetary orbit, and it makes perfect sense to ask a planet how many orbits it's made in a duration. Hence the I would like to combine the 2 implementations of |
Looking at the Ruby implementation they can use the |
Brings the test, example and stub up to my current approach, which is outlined exercism#177 (comment)
I've updated the implementation. And I've put a To Do list at the top of the PR so that I can track progress. |
Problem order specified. I think its current position is OK. |
use space_age::*; | ||
use num::{Float, NumCast}; | ||
|
||
fn assert_in_delta<T: Float + Display>(expected: T, actual: T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose I'm not afraid to add a dependency if it's necesary (that's what cargo is for) but I want to hear the explanation for why it is necesary, rather than have fn assert_in_delta(expected: f64, actual: f64)
. What does the bound of Float
get us that we will not otherwise have? Does it allow students more flexibility in the return type of their years_during
function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there's an aspect of Rust I'm missing here. I want this function to accept both f32 and f64 and the only way I could see to do that is by using the Float
trait in num
Also, the NumCast
part of num
comes in pretty handy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want this function to accept both f32 and f64
Is that because you think students may change years_during
to return an f32
instead of an f64
(as it is so defined in the stub file) and you want to be prepared for this situation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. I swear I was having problems with comparisons in some cases, but now I can't re-create them. I'll drop the crate and see if I run into any problems.
One of the to-do items has baffled me
I've tried tackling this a variety of ways, all with no luck. Anyone know how to implement From for all numeric types? I would thinks something like: impl<T: Num> From<T> for Duration {
fn from(s: T) -> Self {
Duration { seconds: s }
}
} Would work. But no. I get "non-scalar cast: |
I'm on the fence about 81fa6ee. Interested in what others think of the idea. If we're OK with the API then this should be ready to go. |
I know you aren't thinking about the genericity over I do suppose the I have no complaints about the problem placement or topics.md I imagine there may be questions like "why isn't |
#[test] | ||
fn earth_age() { | ||
let duration = Duration::from(1_000_000_000); | ||
assert_in_delta(31.69, Earth.years_during(&duration)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting point that Earth.years_during
is used rather than Earth::years_during
. This is possible because Earth
is a struct with no members (a unit-like struct in the nomenclature). Doesn't seem to me like these functions need to take a self
though...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allows for the "unusual" solution of https://gist.github.com/petertseng/ec3887c899f0259661847e8fbfba820d
I think we're always welcoming of suggested changes like this. We can always point to PRs like this one (or the commit history) to say, "Here was the goal of this API". And if people can provide a good alternative that satisfies the same goals, then all the better.
Agreed that |
This implementation follows the common test suite. We designed the tests to encourage (force?) students to implement solutions using Traits, and for them to use the From trait to convert numbers to Durations. In a "Real World" implementation of a Planetary Calendar you might not implement this code in this same way. That's fine! Exercism isn't here to model Real Production Code. It's here to help students learn about language, design and tradeoffs. By focusing on Traits this problem will hopefully illustrate how Rust handles design -- no Inheritance, just Composition via Traits -- and minimizing code duplication. We've placed this problem after Queen Attack and Roman Numerals. If the student has gone through the problems in order then they've covered Traits (Roman Numerals, and maybe in Queen Attack). Custom traits may be new, depending on how they handled Queen Attack. I think providing a default implementation for a trait function will be new, but the stub file makes it pretty clear what's going on there. I don't think it's a big leap to add a trait function that will be implemented differently for each implementation of Planet. But we'll see what students make of it. We're also trying something new, adding a topics.md file that will list what parts of Rust the problem & solution focus on Lengthy discussion of how this problem was implemented can be found at exercism#177
94fa3dc
to
4dc0b58
Compare
Ok. I think this one is winding down so I've rebased down to a single commit. I'll let this PR sit until Friday, August 19th. If nothing comes up, I'll merge it. |
First version that passes the tests.
This first commit is intended so we can discuss the crate's API and the tests. There are two decisions I made here that I can see people finding odd.
From
trait, but I can also see implementations grappling with shared behavior between the various planets that can be handled via traits.Testing that a struct is equal to a floatThis was dumbI'm not bound to either of these decisions, but they were intentional.
To Do Before Merge
Update example code to implementSkipping, not necessary and probably requires theFrom<u64>
andFrom<f64>
genericallynum
crate anyway.