Skip to content

Add exercise book-store #390

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
Nov 11, 2017

Conversation

coriolinus
Copy link
Member

This was a fun challenge to implement in Rust, but it also serves
as a useful demo of the code generation implemented in #389:
past implementing the example, I had to edit five lines of code
in tests/book-store.rs and three lines to add the stub in src/lib.rs.
Everything else was generated automatically.

Comments are invited pertaining both to this exercise and to the output
of the exercise generator.

This was a fun challenge to implement in Rust, but it also serves
as a useful demo of the code generation implemented in exercism#389:
past implementing the example, I had to edit five lines of code
in tests/book-store.rs and three lines to add the stub in src/lib.rs.
Everything else was generated automatically.

Comments are invited pertaining both to this exercise and to the output
of the exercise generator.
@coriolinus
Copy link
Member Author

Looks like the generator made a bunch of edits to config.json beyond the insert which we actually care about. I'll take those out by hand, for this exercise; they're beyond the desired scope. However, I'm going to add them into #389; due to the fact that the generator parses and re-emits config.json, it will always inescapably act as a JSON prettifier, and we don't want to force people to manually take out the prettying edits that it makes.

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.

I don't imagine that I'll have too much to say about the example solution, but I do about the generated tests. let's take a look.

book_groups.push(Group::new_containing(*book));
}
book_groups.sort();
println!("DecomposeGroups::new() --> {:?}", book_groups);
Copy link
Member

Choose a reason for hiding this comment

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

still need this?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, but it does get captured by the test runner, so I didn't bother to take it out. That said, I don't object to removing it.

/// All cases for the `total` property are implemented
/// in terms of this function.
///
/// Note that you'll need to both name the expected transform which
Copy link
Member

Choose a reason for hiding this comment

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

Should it be the case that the student still sees this? They may think the "you" refers to them.

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'll take out the comments aimed at the exercise implementor.

/// Note that you'll need to both name the expected transform which
/// the student needs to write, and name the types of the inputs and outputs.
/// While rustc _may_ be able to handle things properly given a working example,
/// students will face confusing errors if the `I` and `O` types are not concrete.
Copy link
Member

Choose a reason for hiding this comment

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

Should it be the case that the student still sees this? They may be confused because I and O are nowhere in sight here.

/// While rustc _may_ be able to handle things properly given a working example,
/// students will face confusing errors if the `I` and `O` types are not concrete.
///
/// Expected input format: ('basket', 'targetgrouping')
Copy link
Member

Choose a reason for hiding this comment

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

Should it be the case that the student still sees this? They may be confused because basket and targetgrouping are nowhere in sight here

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 note to the exercise implementor, but in this case I think we should leave it for clarity for those students who examine the tests.

On test generation, the generator collects the cases. If the case doesn't already have an input property, it generates one as a tuple containing all the unknown properties in the case. However, this discards information: as properties, these items have names; as tuple fields, they do not. The comment is inserted to help the implementer decipher the input tuple.

Copy link
Member

@petertseng petertseng Nov 10, 2017

Choose a reason for hiding this comment

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

I was about to suggest

Ah, then this should be process_total_case(basket: Vec<usize>, targetgrouping: Vec<Vec<usize>>, expected: f64)

but now I hesitate a little bit about that suggestion because then it might not be clear which of these are inputs, whereas it is clar if they're named input:

It's also unfortuntae because targetgrouping is not an input. Maybe it shouldn't be in the canonical-data.json at all. But we can continue this discussion pretending it's an input, based on what we would like to see for exercises that actually do have multiple inputs.

Copy link
Member Author

Choose a reason for hiding this comment

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

The principle I used when writing the generator is that in cases where we require manual intervention from the test implementor, the effort required should be minimal. The actual generated code looks like this:

fn process_{property}_case<I, O>(input: I, expected: O) {
    // typical implementation:
    // assert_eq!(
    //     student_{property}_func(input),
    //     expected
    // )
    unimplemented!()
}

While it would be possible to break that out into a more complex function signature, as you say, it becomes less obvious for the test implementer which parameters form the actual inputs. I prefer to leave them as a simple tuple, for now.

/// students will face confusing errors if the `I` and `O` types are not concrete.
///
/// Expected input format: ('basket', 'targetgrouping')
fn process_total_case(input: (Vec<usize>, Vec<Vec<usize>>), expected: f64) {
Copy link
Member

Choose a reason for hiding this comment

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

I note that input.1 is never used, but I'm not particularly concerned. Might make one wonder "why not just not pass it into this function, then?"

Copy link
Member Author

Choose a reason for hiding this comment

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

As commented above, the input tuple contains all the case properties which we don't know. In this case, I believe that the targetgrouping field was meant to be an output hint.

I considered having the student return their groups and comparing them to the target groupings, but I decided against it. Checking the computed lowest price only gives the same assurance as checking it with the groups, but with a considerably simpler interface.

@coriolinus coriolinus merged commit 415016c into exercism:master Nov 11, 2017
@coriolinus coriolinus deleted the add-exercise-book-store branch November 11, 2017 12:48
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.

2 participants