Exercism.io challenge — Crystal track

Jacek Brożek
10 min readAug 11, 2019

Fast as C, slick as Ruby

That is what Crystal’s authors claim on their website. As a Rubyist and Poznan Ruby Users Group organizer, I had a chance to hear about Crystal in the past. “Crystal for Rubyists” sounds familiar, huh? I must admit the project looked interesting back then. That was a very early stage though, so I’m particularly curious about the current state of this technology.

Getting started

I’m a Mac user, so installation with Homebrew was trivial, but it also seems other platforms’ users can count on a solid support.

https://crystal-lang.org/docs/installation/

Also before starting, I had to install Exercism CLI, but installation wasn’t any harder.

The Crystal track on Exercism.io includes 24 exercises in total, so let’s have a look at the most interesting ones.

Hello world

This paragraph is going to be a common section for all of the tracks we’re going to follow. Let’s start simple.

An example includes specs/ directory, so let’s check what’s in there first. We run an example, and that’s what we see:

All right, this one looks familiar. What we need is a HelloWorld class implementing a class method (which you might know as a static member function) which then returns a “Hello, World!” string.

We rerun specs, and everything works perfectly fine!

That wasn’t a too ambitious example, but that’s not what we expect from Hello world section. At least we know that we’ve set up everything correctly and we can move on to the next examples.

Hamming

The next task expects from us to calculate the Hamming difference between two DNA strands. I’m not going to go deep into explaining each task unless it’s complicated — I think specs are enough to show the authors’ expectations.

At first, I wanted to skip writing about this task as this is only comparing strings, but my roots have slapped me in my Ruby face already…

So, let’s have a look at tests again:

Oh, you hawk-eyed smartass! Class with a class method again! The only difference is two string arguments that this method accepts. Also, some of the specs are pending which means you’ll get informed about them in the report, but they’re not going to get executed. I’ve changedpending to it straight away to have a full picture.

We’re not going to overcomplicate our solutions, so let’s go for comparing strings by their characters. As Crystal has not only Ruby-inspired syntax but also implements many of its core features, I assumed there must be a String#split method. To make sure, I run the crystal play server, which is an interactive interpreter to play with Crystal. It turned out I was right:

Few lines of codes later we’re good to rerun specs. That’s what I’ve ended up with:

That will do the job, I’d expect. I was waiting for only one example failing in specs as we’re not performing any validations just yet. The result was a bit of a surprise:

I got back to the playground once again and analyzed the solution. Didn’t have to wait long for the first facepalm. Did you compare a String with a Char, you dumbass? Well, it’s time to forget about Ruby for the moment…

Now we’re home.

Only one failing spec, let’s add validation and move on.

We’re done here, I’ve added a private method that checks sizes of both strands and also explicit return type as an Integer for the compute method — we have types, let’s use them! We didn’t have to specify the error message, neither extract the method, but I think it’s a good practice concerning readability.

Great success! Now let’s wait for the mentor’s approval.

Side quests!

Completing the first two exercises unlocked a few extra tasks. While three of these were quite basic (algorithmic problems mostly), the one looks pretty interesting. It was an introduction to reactive programming — the name itself is cool, let’s see how Exercism.io team describes it:

Reactive programming is a programming paradigm that focuses on how values are computed in terms of each other to allow a change to one value to automatically propagate to other values, like in a spreadsheet.

Fair enough, I think it’s easy to get the basics. Specs are quite extensive and detailed, so let’s have a look at them in parts.

All right, starting with writing down the assumptions:

  • every class is being namespaced by React module
  • InputCell should have value property of Int32 type
  • ComputeCell takes up to two inputs and a block, where inputs can be either an InputCell or other ComputeCell objects
  • changing a value of an input affects the output ComputeCell‘s value

I think the first two points are quite straightforward, but a solution to the next one didn’t come easy. Again, I’ve started with what I’d typically do writing the same code in Ruby — create a Binary and Unary compute cells and check the arity using *args splat to accept a variable number of arguments. Fortunately, it didn’t take long before I reminded myself that I have the method overloading. I wasn’t too harsh to myself; I haven’t seen it for a while. I decided to be smart and read the documentation before taking another try implementing the solution:

I thought going back to static typing is going to be more of a trouble to me, but it turned out it’s still quite intuitive. Also, method overloading can produce neat solutions instead of adding more complexity while using conditional statements. When we add a Ruby-like syntax to all of these, Crystal gets a huge point here.

All right, that was the easy part of the task, let’s move to the callbacks. I’m not going to paste all the test examples, but here goes a few that describe the rest of the task the best.

All we need to do is to add the ability to register callbacks to the ComputeCell and later on fire all the callbacks on input value change. As everything happens in-memory, I’ve decided to add some dependency management, that holds all the associated objects which we would like to inform about the change. That’s why I went for a class instead of a struct for the InputCell as we pass the class objects by the reference. Hence the changed value can be picked up by its parent.

Okay, it doesn’t do much, but it’s sufficient chunk of code to satisfy our needs. That’s how we’re going to use it inside the InputCell.

We simply inject our DependencyManager’s instance in a constructor, then proxy add_dependency method and add the basic propagation after we set the value property. Then all we need to do is exactly the same thing with dependency injection in the ComputeCell, but this time we’re propagating the value change info before following callbacks in the chain are being run. Also, we add dependencies to input cells. That’s how are complete ComputeCell is going to look like afterward:

All right, we’re done. We may notice that we’ve also added methods for adding and removing callbacks, as well as some value checking which was another part of the task.

Fast forward

We need to stop here for a moment. At first, I’ve participated in Mentored Mode, but there are many participants and only one Crystal mentor, so I decided to switch to the Independent Mode. That means I’m not going to get any feedback from the Mentor, but I’ll get instant access to the next exercises, which should help me keep the pace of writing this series. So here we push the button!

Actually, I switched back to the Mentored Mode again, but only because the UI was better and the tasks seemed to be more organized. There’s a button to complete an exercise without waiting for the mentor’s feedback though — I’m still not going to bother anyone with my solutions.

First one done, 47 to go

It feels good, as I have completed all of the 11 core exercises and 13 side ones. Surprisingly, I feel kind of satisfied even though I haven’t taken a chance to receive any feedback from mentors. Anyways, I compared every solution with those sent by the community, and it turned out mine didn’t look too bad!

Time to sum it up

Did I mention it’s going to be an opinionated and subjective series? Good.

I was wondering what would be the best way to summarise things I’ve learned, but nothing creative came to my mind. Well… Nobody’s perfect! I think it’s worth to share my thoughts about both Exercism.io track and Crystal itself. Let’s start with the language then.

Crystal, oh Crystal!

As I’ve mentioned a few times in this article, I have strong Ruby background, so jumping in into Crystal’s basics wasn’t too much of an effort. The syntax is very similar, and most of Ruby’s API has also been implemented in its statically typed fellow. For someone without such foundation and familiarity with Ruby-like syntax, it would be good to check a few resources. I think there are more materials and guides about Ruby, so feel free to start there, especially when you’re not familiar with Iterable and Enumerable modules as these are a bit different constructs from what you might’ve seen in the other languages.

Also I kind of enjoyed working with statically typed language, although in a few cases I was missing some dynamic programming concepts — I thought I’d find it annoying at some point, but the transition was quite natural. I think it wouldn’t be a big deal if I were more familiar with non-dynamic patterns and practices — may be in the next parts of this series I’ll have more chances to catch up on those! P.S. I have tried macros, but it didn’t solve my problems.

Overloading. Overloading is the shit — this thing I miss the most in my dynamic-programming-driven life. I haven’t needed any case-when statement during my Crystal track, and I give great credit to overloading — not sure if you feel the same, but that’s the dirtiest of all control expressions.

Null/Nil checking eliminates null pointer exceptions and enforces you to write the code in the way you can’t easily end up with such error. Did I like it? Well, looking at the issue trackers I use, I should. On the other hand, what’s the life without a bit of craziness? However, this thing made me use my brain more often, but I find it more useful than annoying. I wonder how it is to live without runtime errors…

Last, but not least — environment. At first, I thought I’d need some IDE to debug problems in runtime, but it turned out Crystal has a cool thing called Playground. I used it quite extensively to test my solutions and play with new things. Also, I’ve checked the shards collection (no need to use them in this course), but the ecosystem itself looks promising. Moreover, there’s a thing I’ve mentioned at the beginning — setup was dead-easy.

P.S. Crystal is crazy-fast as well.

Did the track do the job?

At first, I’d like to focus on Core Exercises which were not as entertaining as I’d think. Maybe I had too high expectations, or maybe being familiar with Ruby-like syntax made it look easy. Like in academics — there were math, algorithmic and data structure problems, which are quite usual for all the kinds of entry-level courses. I was missing real-life examples, but fortunately, there were extras.

Side Exercises to the rescue! Aside of the React one that you could read about above, there was one about implementing Binary Search Tree, which made me to implement both Iterable and Enumerable interfaces — also the coolest algorithmic and data structures related task. Another one worth to mention is the Forth language evaluator — maybe not the most ambitious or useful one, but it took me a while to figure out the solution. Completing this task was very satisfying though.

Overall, the track didn’t exhaust a Crystal topic (I’d still put my hands on Fibers, macros and maybe some simple C-bindings) and was more of an entry-level course, but it encouraged me to play with this language a bit more in the nearest future.

Where to go next?

As it was fun, I want to carry on completing the next tracks. The one waiting in the line is Perl 6. I’m scared a little with this one, but we’ll see how it goes.

--

--

Jacek Brożek

Programmer, solution architect, engineering manager. With special place for Ruby in his ❤️