Skip to content
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

[#722] New practice exercise yacht #760

Merged
merged 3 commits into from
Jun 15, 2021

Conversation

elibdev
Copy link
Contributor

@elibdev elibdev commented Jun 13, 2021

This is my first contribution to this project, so I'm not sure if I've done everything right. I haven't edited the track config because I'm not sure how the difficulty would be rated. I would be happy to make any necessary changes to this PR. Thanks!

@github-actions
Copy link
Contributor

Thank you for contributing to exercism/elixir 💜 🎉. This is an automated PR comment 🤖 for the maintainers of this repository that helps with the PR review process. You can safely ignore it and wait for a maintainer to review your changes.

Based on the files changed in this PR, it would be good to pay attention to the following details when reviewing the PR:

  • General steps

    • 🏆 Does this PR need to receive a label with a reputation modifier (reputation/contributed_code/{minor,major})? (A medium reputation amount is awarded by default, see docs)
  • Any exercise changed

    • 👤 Does the author of the PR need to be added as an author or contributor in <exercise>/.meta/config.json (see docs)?
    • 🔬 Do the analyzer and the analyzer comments exist for this exercise? Do they need to be changed?
    • 📜 Does the design file (<exercise>/.meta/design.md) need to be updated to document new implementation decisions?
  • Practice exercise changed

    • 🌲 Do prerequisites, practices, and difficulty in config.json need to be updated?
    • 🧑‍🏫 Are the changes in accordance with the community-wide problem specifiations?
  • Practice exercise tests changed

    • ⚪️ Are all tests except the first one skipped?
    • 📜 Does <exercise>/.meta/tests.toml need updating?

Automated comment created by PR Commenter 🤖.

@neenjaw neenjaw self-requested a review June 13, 2021 07:43
@@ -0,0 +1,21 @@
# Yacht
Copy link
Contributor

Choose a reason for hiding this comment

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

This file can be deleted

[
app: :yacht,
version: "0.1.0",
# elixir: "~> 1.12",
Copy link
Contributor

Choose a reason for hiding this comment

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

While we encourage students to write using 1.12, our solution needs to be compatible back to 1.8 for the time being

Suggested change
# elixir: "~> 1.12",
# elixir: "~> 1.8",

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, I'll edit this and make sure it works with 1.8. Thanks!

Comment on lines 1 to 143
defmodule YachtTest do
use ExUnit.Case

@data Application.app_dir(:yacht, "priv/canonical-data.json")
|> File.read!()
|> Jason.decode!()

for test_case <- @data["cases"] do
test test_case["description"] do
assert Yacht.score(
unquote(test_case["input"]["category"]),
unquote(test_case["input"]["dice"])
) == unquote(test_case["expected"])
end
end
end
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is pretty cool, but this is incompatible with the v3 test runner at present because it would require jason to be available and as it is presently we don't have that build capacity. I don't want you to throw this away though, as I think there might be an application for this approach at some point.

In the mean time can you create a test suite file similar to how the other practice exercises are laid out?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's really awesome that you attempted to write a test generator! It is something that we would want to have in the track at some point, but it's a really big project that requires good planing beforehand and knowledge of how the track and the platform works. If you are interested in doing that, we should open a GitHub issue and talk about the requirements. There was a previous attempt that was abandoned (https://github.com/exercism/elixir/pull/521/files).

One of the requirements would be that the test generation needs to happen beforehand, not on the student's computer. The student needs to receive a ready test file with all of the test cases. The test file, in most exercises, is the only way to learn the requirements for the solution. The student shouldn't need to read canonical-data.json to know how to solve the exercise. The student ideally should not even see the canonical-data.json file 🙂. We want to encourage TDD, so the student also needs to be able to un-skip every test case one by one, and that's why all test files have pending tags for all tests but the first one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for explaining the context around this and why this isn't a valid approach for the way the exercises work. I will definitely update this PR to include a test suite with the tests all explicitly written out so it will work as expected for students. I am interested in helping with that test file generation project, but I understand how it's separate and outside the scope of the exercises.

Comment on lines 22 to 27
defp deps do
[
{:jason, "~> 1.2"}
]
end
Copy link
Contributor

Choose a reason for hiding this comment

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

We can't use external dependencies in the test runner presently. See note down below about this

Copy link
Contributor

Choose a reason for hiding this comment

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

Not only the test runner cannot handle external dependencies, we also have no process of explaining to students how external dependencies work and how to use them. If we were to allow external dependencies in exercises, we would need to edit a lot of student documentation on how to run the tests (e.g. mention that some exercises need mix install before doing them) because many students are completely new to Elixir. It would also add a big problem of dependency version updates. We want to keep a broad range of Elixir versions in which the exercises can be solved, and sometimes it might not even be possible to have a single dependency version that runs on all Elixirs from 1.8 to 1.12. For all those reasons, we made a decision not to use any external dependencies for code that the student needs to run.

I think it's our fault that we didn't communicate this properly beforehand. This should be mentioned in CONTRIBUTING.md 🤔. I'll open a PR.

PS: let's not forget to remove the mix.lock file from this PR 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No problem, I will update the PR without the dependency. I mostly did this as a fun exercise for myself so please don't worry about it not being documented explicitly (the contributing documentation is very helpful). I had a feeling it might not fit in the project as-is but I'm happy to make the changes so it does.

@@ -0,0 +1,48 @@
# Generate tests.toml file from canonical-data.json
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting approach here, we haven't done anything like this yet to generate test files or test toml configurations so I think this might not work with our current set up for v3. Let me take a think what might need to change to accommodate this method.

Copy link
Contributor

Choose a reason for hiding this comment

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

I learned 2 days ago that Exercism already has a tool that generates tests.toml files. It's a feature of configlet. @elibdev it's another failure on my part that I didn't mention this anywhere in the ticket or in the track's documentation. I'm going to open a PR that explains toml files and how to generate them.

Great work on your part for automating this 👍 but this script can be deleted now 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great, I'll delete this. Thanks!

Copy link
Contributor

@angelikatyborska angelikatyborska left a comment

Choose a reason for hiding this comment

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

Thank you a lot for the contribution 🎉. I am really sorry that I didn't write exhaustive documentation right away, which caused you to try very reasonable and clever automated approaches that need to be undone. I hope you have the time and motivation to do the changes, we would love to merge this PR with a new exercise 💜

I haven't edited the track config because I'm not sure how the difficulty would be rated.

Judging by your example solution, I would say... a 5? It's just a guess, it doesn't have to be perfect 😬

For "prerequisites", I would suggest:

"lists",
"cond",
"case",
"if",
"multiple-clause-functions",
"pattern-matching",
"guards",
"enum"

And for "practices", I would suggest "enum", but you would also need to remove "enum" from "practices" of another exercise, for example... Zebra Puzzle? There is a limit that a concept can be practiced by maximum 10 exercises.

Comment on lines 22 to 27
defp deps do
[
{:jason, "~> 1.2"}
]
end
Copy link
Contributor

Choose a reason for hiding this comment

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

Not only the test runner cannot handle external dependencies, we also have no process of explaining to students how external dependencies work and how to use them. If we were to allow external dependencies in exercises, we would need to edit a lot of student documentation on how to run the tests (e.g. mention that some exercises need mix install before doing them) because many students are completely new to Elixir. It would also add a big problem of dependency version updates. We want to keep a broad range of Elixir versions in which the exercises can be solved, and sometimes it might not even be possible to have a single dependency version that runs on all Elixirs from 1.8 to 1.12. For all those reasons, we made a decision not to use any external dependencies for code that the student needs to run.

I think it's our fault that we didn't communicate this properly beforehand. This should be mentioned in CONTRIBUTING.md 🤔. I'll open a PR.

PS: let's not forget to remove the mix.lock file from this PR 🙂

Comment on lines 1 to 143
defmodule YachtTest do
use ExUnit.Case

@data Application.app_dir(:yacht, "priv/canonical-data.json")
|> File.read!()
|> Jason.decode!()

for test_case <- @data["cases"] do
test test_case["description"] do
assert Yacht.score(
unquote(test_case["input"]["category"]),
unquote(test_case["input"]["dice"])
) == unquote(test_case["expected"])
end
end
end
Copy link
Contributor

Choose a reason for hiding this comment

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

It's really awesome that you attempted to write a test generator! It is something that we would want to have in the track at some point, but it's a really big project that requires good planing beforehand and knowledge of how the track and the platform works. If you are interested in doing that, we should open a GitHub issue and talk about the requirements. There was a previous attempt that was abandoned (https://github.com/exercism/elixir/pull/521/files).

One of the requirements would be that the test generation needs to happen beforehand, not on the student's computer. The student needs to receive a ready test file with all of the test cases. The test file, in most exercises, is the only way to learn the requirements for the solution. The student shouldn't need to read canonical-data.json to know how to solve the exercise. The student ideally should not even see the canonical-data.json file 🙂. We want to encourage TDD, so the student also needs to be able to un-skip every test case one by one, and that's why all test files have pending tags for all tests but the first one.

@@ -0,0 +1,48 @@
# Generate tests.toml file from canonical-data.json
Copy link
Contributor

Choose a reason for hiding this comment

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

I learned 2 days ago that Exercism already has a tool that generates tests.toml files. It's a feature of configlet. @elibdev it's another failure on my part that I didn't mention this anywhere in the ticket or in the track's documentation. I'm going to open a PR that explains toml files and how to generate them.

Great work on your part for automating this 👍 but this script can be deleted now 🙂


def score("full house", dice) do
full_house =
Enum.frequencies(dice)
Copy link
Contributor

Choose a reason for hiding this comment

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

This function is only available from Elixir 1.10. One of the challenges of the example solution is that it needs to work in older versions too, currently we start testing from 1.7. I'm going to update the documentation to make this more explicit.

We had this problem with another exercise already 🙂 here:

defp frequencies(strings) do
# Enum.frequencies is only available from Elixir 1.10
strings
|> Enum.reduce(%{}, fn word, count ->
Map.update(count, word, 1, &(&1 + 1))
end)
end

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, I'll update my solution to not use Enum.frequencies/1 and will test it on Elixir 1.7 to make sure it works with that as well.

@elibdev
Copy link
Contributor Author

elibdev commented Jun 13, 2021

Thanks for all the feedback! I'll make the necessary changes that you've helpfully laid out. For the track config "practices" would it be better if I changed my solution to not use the Enum module as much and use for instead, if there are too many exercises that require Enum?

@angelikatyborska
Copy link
Contributor

For the track config "practices" would it be better if I changed my solution to not use the Enum module as much and use for instead, if there are too many exercises that require Enum?

No, don't worry about it! I think it's the nature of Exercism exrcises and Elixir's standard library that Enum plays a big role in almost all of them 😉. As far as I know, only a small subset of operations on enumerables is easier to achieve with for than with the Enum module (e.g. creating cartesian products). For this exercise, Enum is better suited, so let's keep it.

@elibdev
Copy link
Contributor Author

elibdev commented Jun 15, 2021

A couple things I wasn't sure about:

  • In the track config, I just generated a new UUID v4 fo the exercise. Is that exercise UUID supposed to come from somewhere instead?
  • I put the "practices" as "multiple-clause-matching" so we don't have to change an existing Enum exercise. I think for this exercise the multiple clause matching does apply but I'm not sure if that's the right one to pick.

Copy link
Contributor

@angelikatyborska angelikatyborska left a comment

Choose a reason for hiding this comment

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

Excellent ❤️ I left a small (optional) suggestion. The configlet / lint is failing due to a typo in a filename, nothing serious.

Is that exercise UUID supposed to come from somewhere instead?

No, generating a new unique value is correct 👍

I think for this exercise the multiple clause matching does apply but I'm not sure if that's the right one to pick.

Sounds like a good pick to me! It's really hard to tell at the beginning. For existing exercises, we analyzed some community solutions to see which approaches people use the most. Here, we only have one solution... but I feel good about suggesting to students that they should solve this one with multiple clause functions 😄

@@ -0,0 +1,143 @@
defmodule YachtTest do
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a typo in the file name

exercises/practice/yacht/lib/yacht.ex Outdated Show resolved Hide resolved
Co-authored-by: Angelika Tyborska <[email protected]>
@angelikatyborska
Copy link
Contributor

All green = merge 🚀

Thank you again 💜. The next exercise won't be available on exercism.io until v3 is launched, but you will be able to check it out on v3 staging (https://exercism.lol/tracks/elixir).

If you enjoyed adding a new exercise, I added a few more issues about more practice exercises 😇

@elibdev
Copy link
Contributor Author

elibdev commented Jun 15, 2021

Thank you for all your help, and I will check out the new exercise issues! 💜

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
x:size/large Large amount of work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants