-
-
Notifications
You must be signed in to change notification settings - Fork 159
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
How to handle input validation and other exceptions in exercises? #670
Comments
@tasxatzial My favourite option at the moment is to use asserts for its simplicity and then cover the other options in a concept dedicated to error handling to explain how to do it in the other way described above. What is your opinion, your preference? |
I have not included the option of using specs because they are even more complex, and I think this should be introduced in a dedicated concept. |
I agree that we should be choosing the simplest approach. However, since exceptions aren't really the main focus of the exercises, we should carefully consider whether breaking all solutions simply because we changed the returned error is worth the trouble. Once the solutions are invalidated, people are unlikely to update them, especially since Clojure isn't very popular. Consequently, new learners will end up looking at broken solutions without any indication of why they are broken. This doesn’t sound like a small problem. Additionally, we should keep in mind that syncing to the specs will already invalidate all solutions for specific exercises for reasons unrelated to error handling. Idiomatic return value: I would only consider returning Assertions: Whether they simplify the code or not should never be a decisive factor, even if Clojure code already appears messy. One more level of nesting doesn't make a significant difference. If it does, that's an opportunity for refactoring. Additionally, none of the 80 exercises I have locally use assertions; all of them use exceptions. Therefore, switching to assertions would immediately break all solutions without providing any real added value. Skipping tests: I'm always in favor of skipping an error test case instead of adding it and breaking all solutions. For example, the My preference: Return a simple Bottom line: I would prefer that old exercises are invalidated only for very good reasons, such as tests being completely outdated or inconsistent. For new exercises, options like exceptions, nil, semantic errors, and skipping tests are all on the table. |
After thinking about it for a while, i'm realizing that solving this problem is vanity. Take a look at how different Clojure functions behave:
In the first example, we are trying to access an element at index -1, and the In the second example, In the third example, we pass a number to flatten, which obviously won't work since the function operates on sequences. Yet, it returns an empty list. At this point, it looks like we should stick with what we already have. This does not seem like a problem we'll be able to solve in a satisfactory way. |
Context
Some exercises require input validation. A simple example (copied below) from the grains exercise.
At the moment, the Clojure track lacks a consistent approach to what to expect from learners and, therefore, how to implement the tests.
A consistent approach is important to allow learners to focus on the core of the exercise, on the concept which is being presented without needing to learn various ways of error handling that are possible in Clojure. Error handling should be explored with specific examples in an error-handling concept.
For the same reason, we should choose a simple or the simplest approach.
But it should also be idiomatic so that we are not introducing practices which are not natural for the language.
Our options
Option 1 - return error value
Some of the current exercises return an "error value", for example in the phone number exercise have:
This could be standardised to include the expected description of the error:
Pros: The implementation of the
phone/number
could be left to the learner. Any flow control would do.Cons: It is generally not a good practice as error handling might be difficult.
Option 2 - asserts
A relatively common pattern in Clojure is to use
assert
s. An example of testing could be the following:or with the exact message
with the expected implementation implementation something like this:
Pros: Very simple and relatively common approach. Multiple assertions can be added one after the other without increasing nesting.
Cons: Strong voices in the community suggest that assertions use internal Java constructs that were never meant for such use, only for fatal errors that should result in the application stopping immediately.
Option 3 - throwing exceptions
Instead of using assertions we can throw
IllegalArgumentException
or a more generic exception withex-info
. The testing would be the same as in Option 2 - asserts, but the implementation would change to:or with
ex-info
Pros: Perhaps a more standard approach to error handling, but it requires conditionals, constructing, and throwing exceptions.
Cons: More complex, and with multiple assertions it adds a lot to the actual code for the solution.
Option 4 - semantic error handling
This is similar to error codes, Option 1, but with a little bit more structure. Testing could look like so:
With an example implementation code implementing it here:
Pros: An approach that might be familiar from other languages. It provides clear structure to the tests.
Cons: This can be a bit verbose for simple exercises requiring
{:result (whatever)}
for the return.The text was updated successfully, but these errors were encountered: