Skip to content

Revised Lasagna concept exercise #1079

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

colinleach
Copy link
Contributor

Context:

This is my first C submission, so I hope I got it all right! When offering constructive criticism, remember that I am neither emotionally-sensitive nor litigious...

@colinleach
Copy link
Contributor Author

OK, leave it with me. I need to learn how to fix the errors.

I took the liberty of shortening the name to lasagna (not very thoroughly). Let me know if you hate this and want it changed back.

updated filename
added final newline
added final newline
update UNITY_END();
@colinleach
Copy link
Contributor Author

I fixed several of the initial CI errors (updating unity syntax, which seems to have changed a bit since #765), but I'm running out of ideas for all the undefined reference stuff.

Is bin/run-tests set up to handle concept exercises? If I'm reading it correctly, it seems to assume the exemplar is in .meta/example.c:

        # Copy the examples with the correct name for the exercise
        if [ -e ".meta/example.c" ]
        then
          mv .meta/example.c ./"${exercise_name}".c
        fi

        if [ -e ".meta/example.h" ]
        then
          mv .meta/example.h ./"${exercise_name}".h
        fi

I can't see any mention of exemplar.c (standard for concept exercises), nor the .meta/config.json which points to the correct file name.

@wolf99
Copy link
Contributor

wolf99 commented Aug 13, 2025

Hi @colinleach ,
Yep, the alterations in #765 in the run-tests bash script, or some updated version thereof, are also needed so that the build scripts will handle examplars correctly.

The tests run on files .h and .c files that use the exercise name as the file name.
So the run-tests script takes the example.c and example.h files and renames them.
It needs to also do this for examplar.c and examplar.h files.
The end result and basic action is the same in both cases, its just that the input (i.e. the original file names) are slightly different.

This is why in #765 the following is added in run-tests, note the "examplar" file names used:

        # Copy the examples with the correct name for the concept exercise
        if [ -e ".meta/exemplar.c" ]
        then
          mv .meta/exemplar.c ./"${exercise_name}".c
        fi

        if [ -e ".meta/exemplar.h" ]
        then
          mv .meta/exemplar.h ./"${exercise_name}".h
        fi

Possibly the logic of this bash function could be further improved so that it handles examplars OR examples OR it throws and error, rather than just carrying on after zero moves. This further improvement of throwing an error need not be part of this PR though.
Whats happening now is that it checks for examples, none are there, so the files dont get moved, but then it trys to build and run tests. As the files dont exist, the build cant find things - thus the undefined references.

@ahans
Copy link
Contributor

ahans commented Aug 13, 2025

I fixed several of the initial CI errors (updating unity syntax, which seems to have changed a bit since #765), but I'm running out of ideas for all the undefined reference stuff.

It is because you name your example implementation exemplar.c. It's supposed to be named example.c. That's what all the practice exercises use. Once you rename .meta/examplar.c to .meta/example.c, things work fine. Also, to help with debugging, it's worth noting that the test script set up everything under the build directory (which is in .gitignore). In your current version, if you check build/concept/lasagna/lasagna.c, you'll notice that it is the template that the students see and are supposed to change. Since it doesn't have anything implemented yet, you get the linker errors.

I can't see any mention of exemplar.c (standard for concept exercises),

I'm not sure why the C track deviates from the standard naming. Anyway, example.c is used for practice exercises. I think it should be the same for concept exercises as well.

nor the .meta/config.json which points to the correct file name.

I believe this is only used by exercism website tooling to know which files to show to mentors as the standard solution. It is not used by bin/run-tests.

@ahans
Copy link
Contributor

ahans commented Aug 13, 2025

This is why in #765 the following is added in run-tests, note the "examplar" file names used:

Why would we want to support two different naming schemes?

@wolf99
Copy link
Contributor

wolf99 commented Aug 13, 2025

I'm not sure why the C track deviates from the standard naming

example.c is the standard naming for practice exercises.
examplar.c is the standard naming for concept exercises.
Or at least this was the way it was last time I looked at the spec (a long time ago I admit)

@wolf99
Copy link
Contributor

wolf99 commented Aug 13, 2025

Sorry, Ive checked the docs (first time in a long while). you have a valid point @ahans

You can see in the docs here for a practice exercise that it seems to have changed at some point that the example files can use any name that suits the track as long as it is specified in the config.json.
But even in the examples given there they use example as the file name.
(I seem to recall this was a standard approach across all tracks way back when)
https://exercism.org/docs/building/tracks/practice-exercises

I see the same is true now for concept exercises, where again it is fine to use any name as long as the config.json specifies it.
But the examples there use examplar as the file name
https://exercism.org/docs/building/tracks/concept-exercises

So then, we perhaps could align to "example" as the file name even in concepts, then the changes to the run-tests are not needed (thought the error catching improvement may still be valid)
Thoughts @ErikSchierboom , @ryanplusplus ?

@@ -0,0 +1,14 @@
// Please do not edit this file. It will be explained in a later Concept.
Copy link
Contributor

Choose a reason for hiding this comment

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

How about not providing this file at all and instead putting the declarations into test_lasagna.c? Alternatively, we could just #include "lasagna.c" from test_lasagna.c (that's what the C++ version does). Students would see only the lasagna.c template, so it doesn't matter which approach we use. But not having a header file would lower the chance for confusion.

Copy link
Contributor

Choose a reason for hiding this comment

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

@wolf99 @ryanplusplus What do you think?

Copy link
Member

@ryanplusplus ryanplusplus Aug 14, 2025

Choose a reason for hiding this comment

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

@ahans I haven't read through all the comments yet... is the intent to avoid introducing headers at this point? If so, I think this makes sense. It's weird to do either of these workarounds, but only to someone who is already familiar enough with C to be able to reason through the weirdness. I expect anyone who's just learning C to not even notice because they're focused on the basics.

I'd prefer including lasagna.c in the test file if we have to pick a path.


tests.out: ./*.c ./*.h
@echo Compiling $@
@$(CC) $(CFLAGS) test-framework/unity.c ./*.c -o tests.out $(LIBS)
Copy link
Contributor

Choose a reason for hiding this comment

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

Please make sure all non-empty files end with an EOL character.

@@ -0,0 +1,54 @@
#include "test-framework/unity.h"
#include "lasagna.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Relating to my previous comment, we would replace this line like this and remove lasagna.c:

Suggested change
#include "lasagna.h"
#include "lasagna.c"

@colinleach
Copy link
Contributor Author

Why would we want to support two different naming schemes?

It's probably a historical accident, but now most/all other tracks with learning exercises use examplar. If you are expecting multiple contributors to create concept exercises, adding a few lines to run-tests seems less confusing than asking every author to deviate from the documentation?

not having a header file would lower the chance for confusion

Above my pay grade! I'll keep out of this, and comply with whatever the C maintainers decide.

@ahans
Copy link
Contributor

ahans commented Aug 13, 2025

I'm not sure why the C track deviates from the standard naming

example.c is the standard naming for practice exercises. examplar.c is the standard naming for concept exercises. Or at least this was the way it was last time I looked at the spec (a long time ago I admit)

Thanks, I didn't know this. Haven't really looked at concept exercises yet. So at least the C++ track as well as the Python track follow the example for practice and exemplar for concept exercises naming scheme. I don't think it makes much sense and my vote would go towards using example.c everywhere here, but I'm looking forward to hearing thoughts from others. Extending the shell script to copy .meta/exemplar.c in case of a concept exercises wouldn't be a big deal either. But I'm typically in favor of less complexity where possible.

@ErikSchierboom
Copy link
Member

Personally, I'd just use example for practice exercises, as 99% of tracks use that.

@BethanyG
Copy link
Member

So at least the C++ track as well as the Python track follow the example for practice and exemplar for concept exercises naming scheme. I don't think it makes much sense and my vote would go towards using example.c everywhere here

For the Python track, we use exemplar for concept/learning exercises to highlight that the solution given is intended to be an "ideal" or "exemplary" solution to the exercise. One that mentors could use to nudge the student toward "correct" usage. Concept exercises are intended to teach a small and contained number of language concepts, and they're (as best as we can accomplish it) designed to have a "right" answer. Practice exercises are almost always the opposite.

If someone complains that a Python example solution is "incorrect", I almost never let them change it unless it's obviously very old syntax or an long-unused or outright bad pattern. It's just an example to show that the tests are passable.

However, if someone complains that an exemplar solution is "incorrect" for the main version of Python we support -- we take a look at the exercise and change things. The upshot being that concept exercises should be showing students idiomatic and "valid" code/patterns they can use as they learn the language.

We also have more elaborate stub files for concept exercises, with detailed docstrings, and tests that are much more verbose and detailed with their error messages. We try (and most likely fail) to make everything about the concept exercises something students could copy in their own code an not be "called out" as not coding "correct" Python.

The other key difference is that concept exercise tests are not exposed to students in the web UI. So students need to have clearer instructions and stubs for concept exercises, since they won't be able to see additional test expectations.

So naming the two solution types differently helps to keep things easy to identify, since we do treat the two types differently in a bunch of ways. But a test script could as easily key off the repo folders or the config.json for those things.

@ahans
Copy link
Contributor

ahans commented Aug 14, 2025

Thanks for the detailed explanation of the relevant background, @BethanyG, it makes much more sense now.

Even before understanding the intended meaning of example vs exemplar, I would have taken my vote towards unification back, since in the meantime I noticed that the keys in config.json are also different. Having an entry "exemplar": "example.c" in there I wouldn't find appropriate. So naming it exemplar.c and adding support for that to the rest of the tooling is the way to go IMHO. Sorry for the noise and the confusion!

@ryanplusplus
Copy link
Member

Thanks for the detailed explanation of the relevant background, @BethanyG, it makes much more sense now.

Even before understanding the intended meaning of example vs exemplar, I would have taken my vote towards unification back, since in the meantime I noticed that the keys in config.json are also different. Having an entry "exemplar": "example.c" in there I wouldn't find appropriate. So naming it exemplar.c and adding support for that to the rest of the tooling is the way to go IMHO. Sorry for the noise and the confusion!

I agree, my vote is to go with exemplar.c due to the semantic differences and due to the fact that we'll match most other tracks.

@ahans ahans mentioned this pull request Aug 20, 2025
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.

6 participants