Unidiomatic Zig exercises

We can make this change to the self argument type. I think the return type becomes void.

With regards to error handling, it isn’t the focus of this exercise. There is also the open issue exercises: consider replacing some errors with optionals/asserts/assumes #229 suggesting the track may have used errors when we should not have.

We can add a .docs/instructions.append.md saying “Any instructions other than R, L and A may be ignored.”

Other exercises that could encorage error handling better

Isogram could only allow letters, spaces and hyphens in the input. Anything else would be invalid and the function would return an error value.

Non-printable characters in Pangram and Acronym.

Not enough space in the buffer in Raindrops, Reverse String, Bottle Song, Sieve, Twelve Days, House, Run-Length Encoding, Food Chain.

Scrabble Score does not allow to return an error.

Triangle instructions contradict the tests. Tests include invalid triangles but instructions say:

We opted to not include tests for degenerate triangles (triangles that violate these rules) to keep things simpler. You may handle those situations if you wish to do so, or safely ignore them.

I think we should remove this note from the instructions.

Two-Fer is a weird one. It allows to return anyerror. This is bad. We should return only the errors that are relevant to the exercise. The only error condition here is not enough space in the buffer. We should only allow to return one error.

Collatz Conjecture suggests using anyerror. This is bad. We should not encorage incorrect use of anyerror.

Kindergarten Garden does not say explicitly that invalid input is impossible.

Exercises that address the error handling

Nucleotide Count requires input validation.

If the string contains characters that aren’t A, C, G, or T then it is invalid and you should signal an error.

RNA Transcription explicitly say invalid input is impossible.

This exercise does not require you to validate the input DNA strand. Each tested input contains only the characters A, C, G, or T.

Grains requires input validation.

Hamming explicitly requires input validation.

The Hamming distance is only defined for sequences of equal length, so an attempt to calculate it between sequences of different lengths should not work.

Binary Search and Phone Number allow to return null.

Queen Attack, All Your Base, Largest Series Product allow to return an error.

Linked List explicitly say that invalid input is impossible.

For this exercise, you don’t need to think about errors. In particular, the tests will never call pop or shift on an empty list.

Yes, please.

This would clarify things.

At least we could state that the buffer is always big enough. Zig language is very explicit about errors. Zig track can afford explicit instructions.

Wouldn’t that information be readily available in the test suite? The tests should drive the implementation, and if there are no tests for inputs besides R, L, and A, it seems we don’t need to specify this in the append. I generally use appends elsewhere on Exercism to clarify track-specific implementation details so the instructions don’t contradict the tests.

Exercism generally doesn’t do input validation outside of a few notable exercises because it shifts the focus of the student away from the exercise towards error handling. If you do something a lot like invalid inputs across multiple exercises, the student might start to tune out on the reason we’re including it and then treat it as a boilerplate thing to be done. As a result, they might not gain a lot from the inclusion of input validation.

It doesn’t look like Zig implements the error-handling practice exercise. That exercise doesn’t have a canonical test suite so you’d have a lot of latitude in what tests to provide and what types of errors to handle.

1 Like

The Triangle instructions changed in a sync from the canonical instructions. Perhaps comment in an existing problem-specs thread or start a new one.

This approach is dangerously close to “cheating”. I can just copy the answers from the tests and only care about inputs that are in the tests. But this is not what I came here for. I want to put some effort and learn something.

I am okay if someone else solves the problem however they like. Let them have fun. But I want to have fun with error values, error sets and error unions. Many exercises artificially force me out of this experience. I can not return an error value if the tests call my function without try or catch.

One way to fix this without disrupting everyone else’s experience is inferred error set. We can put an ! before return type

    pub fn move(self: Robot, instructions: []const u8) !Robot {

and add try where we call the function in the tests.

    const robot = try Robot.init(0, 0, .north).move(instructions);

The compiler does not complain even if I never return actual error value. This allows the student have fun whichever way they like.

This is good and I agree with the reasons. I like that input validation is not enforced most of the time.

I many other languages you can throw any exception whenever you feel like it. For example, Java allows to throw RuntimeException without declaring them in function signature. Python does not care about declaring exeptions. Tests do not need to change. Student can ignore invalid input or they can throw an exception if they want to have fun with validation.

Zig is different in a way that you need to write tests in a way that allows returning an error union. If you call student’s function without try the student have only the option to ignore invalid input.

What I am trying to ask is the same experience that other tracks have.

Does draft PR #494 reasonably address the issue?

Ideally, students will be able to solve a problem in a variety of different ways. If we mandate fmt.BufPrintError then we would effectively be endorsing bufPrint as the one true solution, taking away much of the fun in Exercism.

2 Likes

Yes. My solution returns std.Io.Writer.Error for example.

One more thing that bothers me is usize sprinkled all over the place. For example Darts currently suggests for score() to return usize. This is bad in two ways.

  • On most popular targets (64 bit) it may waste resources. The maximum score is 10 for which u8 is more than enough.
  • The whole point of usize is pointer arithmetic. It is good for array index. It is not a “default” integer type that is good for every purpose. Habit of using usize for everything may go sideways when you recompile to another target.

I would like to see more types that are independent of compilation target.

2 Likes

If I understand you correctly, if we decided to change all the signatures of all the exercises to “allow for errors” (inferred error set) and then try/catched in all the test classes, then everyone could still decide to ignore errorneous input but you would write more idiomatic code?

1 Like

That might be appropriate for exercises like food-chain.

There is a different sentiment expressed by @ee7-1282 in

exercises(grains): assert, rather than returning error union #336

exercises: consider replacing some errors with optionals/asserts/assumes #229

e.g.

or say that it is the caller’s responsibility to do input validation.

For isogram pangram acronym scrabble-score I would suggest that no change is necessary. Students can read the tests to understand the requirements, and the limited scope of the exercise.

If we look at the Zig standard library, it is not the case that every function returns errors. Some fail with assertions (panics) if preconditions are not met.

4 Likes

We’ve had similar requests for JS/TS/Ruby/Kotlin etc.

What I am not well-quiped with yet is knowing how Zig normally deals with these. Input validation, whilst “fun” for @truly-not-taken may not be the goal, as they also write that it’s a good thing that most exercises don’t expect you to do it.

In fact, it’s always my stance that input validation is almost always arbitrary and adding it doesn’t add much value to the Exercism-scoped exercises. So I am not a fan of introducing it in any exercise.

However, as I understood it, Zig won’t be able to run the code if you do return an exception (for an exeptional case that we don’t test for). Is that a misunderstanding on my end?

2 Likes

Yes.

You understand correcly.

You can compare ArrayList.append() vs ArrayList.appendAssumeCapacity(). One returns an error and the other asserts that the input is valid which means panic in Debug mode.

Both assertion and returning an error value are valid ways to deal with the unhappy path. But exercise instructions often silently suggest to ignore it.

zig zen (link)

  • Edge cases matter.
  • Runtime crashes are better than bugs.

These make me think that input validation is more important in Zig than in some other languages.

I am not advocating to force input validation on everyone. Just allow to have fun with it for those who embrace zig zen.

Please give more information. Twice you have mentioned zig zen but not sure what that is.

I am aware of the zig-zen conflict, though. So this is confusing in the context (or lack of). Can you clarify your use of this, in the two times I see it mentioned?

1 Like

Language Reference - Appendix - Zen

2 Likes

It would be good if instead of zig zen, then, that they were links, rather than indicating they were a command. This is, of course, directed to @truly-not-taken as they are from their messages, not yours. The communication is otherwise saying something that it is not.

1 Like

Now knowing that Zig Zen is the philosophy the language designers use to make their language stand out, I think the discussion at hand is about how much a high ambition philosophy might hinder beginners to get started and on pace with a language.

This very clearly is the case with input validation and error handling. No matter how much idiomatic it is to a language, to focus on any other aspect of a language you should be able to leave it aside. This is educational hand weaving, not risk accumulation in production.

If the language does not allow to specify an interface for optional error handling / input validation, then the interface should not require error handling / input validation from the student. E.g. specifying input types is some kind of input validation, but the students don’t have to worry about handling it.

If adding the try to the tests only opens the option to do error handling, then it would not hinder the learning experience at all. My Zig knowledge is zero, so I cannot tell about that. It should also be “error agnostic” then, so it would allow error handling of any kind.

Maybe one could also discuss from what level of difficulty the exercises may be so idiomatic that they allow to “embrace Zig Zen”. To me, the philosophy of a language expresses itself in the language (yes, even in JavaScript…). Fluency in a language includes a bit of understanding the philosophy so one can solve problems easily using the language, not against the language. But “embracing a philosophy” goes far beyond that point. And that’s not what Exercism aims for.

2 Likes

About a third of the Haskell exercises use Either or Maybe. About two-fifths of the Zig exercises use Error. Slightly more than half the Roc exercises use Result.

Without learning Roc, my impression is that changing a Zig exercise to use try and ! is equivalent to changing a Roc exercise to use Result.

2 Likes