Most of my time was spent trying to understand the requirements from the description. I finally downloaded the source from github and could see what was going on—some missing Markdown made the resulting HTML unclear. I filed a
PR and hope it gets merged at some point. In the meantime,
here is my cleaned up version in case it helps.
I had a lot of fun with this exercise and wanted to walk through what a noob like me went through to refactor from “solving the problem” to a “well-engineering Swifty solution” (caveat: I am still learning what “Swifty” means and have certainly missed many opportunities to leverage the standard library or other resources to solve this exercise. I hope that the approach I used is useful in spite of that.)
I imported Foundation to get the capitalized property. You can see that the pitches() method is just way too long and has repeating code blocks. The patternIntervals() method is also long and bakes values in the switch statement the method that will make it fragile to changes. But it “works” and all the tests pass.
In this revision, I pulled out the intervals into a calculated property and compacted it with some functional techniques. I have mixed feelings about this property, but in the end I didn’t do much more to change this.
In this revision I pull out that duplicated code in the pitches() section into its own method. I also have some unit tests for it in the test file. It’s starting to get “clean” with small, testable methods and good tests.
4th Working Solution
At this point I took a drive to clear my head and think about the code: what would make this more “Swifty”? While I’m just beginning to get a sense of this, I see extensions, typealiases, and protocols as language features that allows cleaner expression and reusability.
This simplifies pitches() further and gives us some clues on how to clean it up further.
5th Working Solution
The last major revision I made is purely syntactic sugar: I wanted to make the little map over intervals in the pitches() method more obvious what is happening, so I extended Array again with a pick method, and I removed the dependency on Foundation with an extension on StringProtocol I found on
stackoverflow (I later found
a similar extension by Paul Hudson on his site, but the SO link is older and I found it first there):
At this point, I love how the pitches() method reads: “take the base scale, rotate it from the tonic note, then pick out the notes based on the scale’s intervals”.
The ScaleGenerator class itself isn’t too bad now: one method, and two calculated properties, each of which has some tests for it.
Other Thoughts
I find a lot of inspiration from other folks on exercism.io (
example) who take a lot of care to think about their solution to make it clean and easy to follow. Much of the enjoyment I find in Swift is due in part to the design of the language and part to the many enthusiasts who are willing to share their work openly.