-
-
Notifications
You must be signed in to change notification settings - Fork 192
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
Port variable-length-quantity exercise. #960
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, and thanks for the PR! As I mentioned in #955, I'm going to wait with merging this until CI has been unbroken in #958. In the meantime, here is some feedback. Also, thanks for all the good questions. They suggest that there are places where we can improve track documentation.
Position in config.json: The order which configlet
(the Exercism tool) was changed in #936 and was summarized in this comment. This should be moved to the track documentation, too.
I apologize for the messy PR review. Normally I would provide a short, concrete list of things that must be changed, but this is the first exercise that is ported after Exercism migrated to v3, and the Haskell track has not been properly migrated yet.
I'll create some issues that address the particular roadblocks that are not solvable within this PR, and then I'll get back to this PR. In the meantime, feel free to address / answer the points I've highlighted.
|
||
# multiple values | ||
"a91e6f5a-c64a-48e3-8a75-ce1a81e0ebee" = true | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that you are more informed than me about the purpose of .meta/tests.toml.
I'd have to get back to you about this, or perhaps you can link to where the format and purpose reads.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I don't either, but many other exercise has this file and its content seems straightforward to follow.
{-# LANGUAGE LambdaCase #-} | ||
{-# LANGUAGE NumericUnderscores #-} | ||
|
||
module Vlq |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about VariableLengthQuality
?
module Vlq | |
module VariableLengthQuality |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have the impression that one word module name seems to be preferred on exercises that have long names, but I couldn't find a good one, Encode
is probably fine, given that Data.Text.Encoding
in text
does have both encoding and decoding functions.
encodes :: [Word32] -> [Word8] | ||
encodes = concatMap encodeOne | ||
|
||
decodeOne :: MonadError DecodeError m => [Word8] -> m Word32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice to have something like this in an example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Something needs to be addressed in this part?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's perfectly fine.
@@ -0,0 +1,22 @@ | |||
name: variable-length-quantity | |||
version: 0.0.0.1 # TODO: what should this number be? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example solutions don't have a version number.
@@ -0,0 +1,23 @@ | |||
name: variable-length-quantity | |||
version: 0.0.0.1 # TODO: what should this number be? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This question cannot currently be answered.
This track's exercise versioning policy is described in the track README:
https://github.com/exercism/haskell#exercise-versioning
Which originally meant "Go to the exercise's canonical-data.json and look up the version
property". But since exercism/problem-specifications#1674, canonical data no longer contains exercise versions. The reason is political and has to do with people disagreeing about what should go into the problem-specifications repository. Some people got upset, the repository was frozen for a year, and the aftermath is that versioning was removed.
Some of the technical argumentation refers to automated test generators:
To prevent breaking changes, the canonical data is currently versioned using SemVer, so in theory test generators could use this to do "safe" updates. In practice though, most test generators always use the latest version to generate their exercises.
Since the Haskell track does not employ an automated test generator, the test generator is a person who does use the latest version, but does so manually. This reasoning does not seem to apply to this track as long as we manually maintain test files.
- [...]
- There is no longer any discussion whether a change is a patch, minor or major update.
- We no longer need the versioning of the canonical data.
tl;dr: Canonical versions were removed, and our exercise versioning policy depends on them, so we need to make a new versioning policy.
{ uuid :: T.Text | ||
, description :: T.Text | ||
, inputAndExpected :: CaseInputExpect ty | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this test suite made better with the use of type families? I'm a little unsure if the point to use them here is because the author finds them natural (in which case one may consider if the novelty budget was exceeded for the average student), or if there is a didactic opportunity here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm probably having too much fun porting exercise tests from problem spec. But then I feel all those uses are simple cases that can serve teaching purposes: promoted datatype to tag Case
so encode test cases and decode test cases can have records whose types are different. And a bit of type level numbers that, in hindsight, is probably a contrived way of saying "pretty printing width depends on type" (I could just implement instance Show (PrettyHex a)
for Word8 ~ a
and Word32 ~ a
, but we can get into overlapping instances once we start talking about what if now we want Show a => Show (PrettyHex a)
, which is definitely beyond scope for just teaching Haskell)
Let me know if adding more comments in code to explain those language features could mitigate your concerns or I should avoid using them all together.
Your comment also raises a question that I think could be nice to be addressed more explicitly: are students suppose to treat source code of tests as a blackbox or they are welcomed to read and learn from them, when I wrote those tests I thought it was the former but it now looks more like the latter.
forM_ testCases $ | ||
\( Case | ||
{ description | ||
, inputAndExpected = (input, expected) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This appears to be a mash-up between named and anonymous product types.
Why is inputAndExpected
not two separate record fields?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I'm just bundling them together to avoid using two type families (originally CaseInputExpect
was separated as CaseInput
and CaseExpected
)
let PrettyHex encoded = encodes' [x] | ||
mask = 0b1000_0000 | ||
in all ((/= 0) . (Bits..&. mask)) (init encoded) | ||
.&&. ((last encoded Bits..&. mask) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if unqualified imports of these operators will be more easily understood.
I.e. .&.
instead of Bits..&.
because of the operator beginning with .
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has to do with QuickCheck also exporting .&.
, I could unqualify it but it'll still raise the question of which .&.
is this one, but arguably this is straightforward to figure out from context.
Turns out this simply needs to be unique to be used in the database and website URLs. Co-authored-by: Simon Shine <[email protected]>
This is not in the scope of this PR, probably do another one. Co-authored-by: Simon Shine <[email protected]>
Thanks for your detailed feedback! I've addressed I'd also love to be more consistent in terms of code formatting if there is a preferred formatting automation. |
Plus this does look nicer.
I'll get back to this in the weekend! :-) |
Closes #959.
Few questions that I'll need help from maintainer:
config.json
, I'm not sure about pretty much all fields in there.