-
-
Notifications
You must be signed in to change notification settings - Fork 100
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
[Squeaky Clean] new char concept and concept exercise #1248
Conversation
Some CI fails at the first attempt. It dislikes the example code in my md files, in this format: 'A' < 'D'
=> val it: bool = true I'll comment out the FSI output and resubmit. I don't know why it thinks the |
Docs are now fixed and passing. It still has an unexplained problem with Also, I suspect the prereqs will need some attention. It only lists |
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 for this! I think the key thing is to get the exemplar right. I feel like it might be a tid too complex for this exercise, so we might need to tweak the exercise a bit.
@@ -0,0 +1,18 @@ | |||
{ | |||
"authors": ["colinleach"], |
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 is what configlet fmt
should be fixing. Try running bin/configlet fmt --update --yes
config.json
Outdated
"prerequisites": [ | ||
"strings" |
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 agree that we probably want to add to this, but let's first get the exemplar implementation right.
concepts/chars/introduction.md
Outdated
To append a `char` to a `string`, convert it to a string first: | ||
|
||
```fsharp | ||
"Part" + string '1' | ||
// => val it: string = "Part1" | ||
``` |
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 is very uncommon and hardly ever used I think. Previously, one would use sprintf
for this, but nowadays string interpolation would be the way to go. Maybe use that as an example?
BTW, I noticed that we don't yet mention string interpolation in https://github.com/exercism/fsharp/blob/main/exercises/concept/log-levels/.docs/introduction.md, which might make sense. We could then have a separate exercise dedicated to string interpolation and its intricacies.
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.
To append a
char
to astring
, convert it to a string first
As you maybe guessed, this was a half-assed last-minute addition that I sensibly left out of earlier drafts. Trying/failing to make the exercise work early in the syllabus when the student doesn't know much.
concepts/chars/introduction.md
Outdated
Using a StringBuilder may be more performant: | ||
|
||
```fsharp | ||
let sb = new System.Text.StringBuilder() | ||
// => val sb: System.Text.StringBuilder = | ||
|
||
sb.Append('F').Append('#') | ||
// => val it: System.Text.StringBuilder = F# {Capacity = 16; Chars = ?; Length = 2; MaxCapacity = 2147483647;} | ||
|
||
sb.ToString() | ||
// => val it: string = "F#" | ||
``` |
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 to think about this a bit more. StringBuilder is very useful but also a bit overkill for most use cases.
concepts/chars/introduction.md
Outdated
Converting between a `list` of `char` and a `string` is simple: | ||
|
||
```fsharp | ||
System.String.Concat ['E'; 'x'; 'e'; 'r'; 'c'; 'i'; 's'; 'm'] | ||
// => val it: string = "Exercism" | ||
|
||
"Exercism" |> Seq.toList | ||
// => val it: char list = ['E'; 'x'; 'e'; 'r'; 'c'; 'i'; 's'; 'm'] | ||
``` |
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 introduces lists, which we would need to have as a prerequisite (might be fine).
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 increasingly thinking that lists would be a good prereq, moving chars
down the syllabus.
I've played around a bit with the exercise, and what if we did something like this: module SqueakyClean
open System
let transform (c: char) : string =
if c = '-' then
"_"
elif c > 'α' && c < 'ω' then
"?"
elif Char.IsWhiteSpace(c) then
""
elif Char.IsUpper(c) then
"-{c.ToLower()}
else
c.ToString()
let clean (identifier: string): string =
String.collect transform identifier This seems to check all the boxes I'd like to see ticked:
The benefits of this approach are:
The downside is:
What do you think? |
Co-authored-by: Erik Schierboom <[email protected]>
Co-authored-by: Erik Schierboom <[email protected]>
Co-authored-by: Erik Schierboom <[email protected]>
Co-authored-by: Erik Schierboom <[email protected]>
Yes! Your exercise suggestion is much better. I'll need to rewrite the instructions and tests (and take StringBuilder out of the blurb), but that should be easy. I'm 9 h behind you, so give me a day or so,
For many reasons, including the small detail that we don't yet have an exercise for this, and the concept is a very short stub. |
Overall, the message I'm taking is that I should pay less attention to the C# syllabus and treat F# as a very different thing. I can't disagree. |
It might work well in some cases! But probably a combination of C# and Elixir would be good. But I'm also happy to review like this. It wasn't much work for me! |
I added a link to the With string interpolation in there, that needs adding to
I added a draft of this handwaving, but I suspect it could be improved. |
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.
Great work!
@@ -0,0 +1,6 @@ | |||
module SqueakyClean | |||
|
|||
open System |
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.
Good idea to already have this opened!
concepts/chars/introduction.md
Outdated
Iterating over a string returns a `char` at each step: | ||
|
||
```fsharp | ||
[| for c in "F#" -> c, int c |] // => [|('F', 70); ('#', 35)|] | ||
``` | ||
|
||
As shown above, a `char` can be cast to its `int` value. | ||
This also works (*at least some of the time*) for other scripts: | ||
|
||
```fsharp | ||
[| for c in "東京" -> c, int c |] // => [|('東', 26481); ('京', 20140)|] | ||
``` |
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.
Maybe instead of having a list comprehension as the example here, we use a higher-order function?
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.
Agree. Update will follow later today.
[<Fact>] | ||
[<Task(1)>] | ||
let ``Clean_empty_string``() = clean "" |> should equal "" |
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 think that it would be useful for the student to have them implement the let transform (c: char) : string
function first, before having to deal with the higher-order function. So we'd have the tests work on single characters first, and then later on have a couple of tests that verify that clean
can work on an entire string.
This also has the benefit that it is more likely that students use the same exemplar solution, which gives great for mentoring.
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.
Agree. Update will follow later today.
Co-authored-by: Erik Schierboom <[email protected]>
Co-authored-by: Erik Schierboom <[email protected]>
I hope these changes address your comments (and I didn't carelessly miss any this time). I also added a couple of minor tweaks to try and make the docs flow more coherently. |
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 think this is excellent.I'm second-guessing myself a little bit on whether we want to do the full string processing at all, but it's probably fine. We can always move that bit out of this exercise and in a new exercise, for example one that has one deal with StringBuilder
As discussed, this PR attempts to follow Issue #868.
It's mostly a port from C#. I had to drop the task using
Char.IsControl
, as F# rejects things like\0
(nul) as characters.The
about.md
is much expanded, with lots more sample code. More controversially, so isintroduction.md
- now too much, perhaps?