Discovering Crystal


Original post can be found in the ApiumHub blog.

Another in the series. Check out other languages we’ve explored in the past at:

A bit of background about me before we begin our exploration of a new programming language.

I’ve been in love with programing languages for a couple of years now. I’m very interested in their semantics, the reasoning of why their designers chose to do X over Y, and why they outright discarded Z. I’ve done my fair share of DSLs in my tenure as a software developer for both fun and profit; so programming languages are the reasonable “step up” to be interested in.

As such I’m always in the lookout of up-and-coming languages.

Why Crystal?

Long time ago, far far away I was working with Ruby on Rails on a project with other very skilled Ruby and Rails developers. As many others working with Ruby, we liked it’s syntax, it’s ease of use, it’s dependencies (gems) but had some issues with it. Every programming language does.

Honestly, performance was not my main driver to look to other programming languages, even though in many benchmarks Crystal does excel.

I was more concerned with safety and documentation. Let me explain.

Safety

Whilst programming in Ruby, I many times had an error where an object I thought was a Hash was actually a block, or a string was in fact a number. Many times a test would just fail or panic, and it took a minute to look at the trace, maybe put a breakpoint. Pry open the execution. Realize there was a branch of the code that did not set a key in an options parameter that got passed along and I did not account for. Fix it and continue coding. undefined method ‘foo’ for nil:NilClass was particularly recurring in my development cycle.

Documentation

Here I’m not referring to the actual docs I can find in Ruby Doc but rather how much information can the IDE help me out with. I’m a big proponent of making tools to help development, but having to hydrate an options hash to send to a gem’s method and having to yield to look at the gem’s code to understand what values are even available, is a big reason why I enjoy typed programing languages so much.

class FooConfiguration {
    String bar;
    Integer biz = 3
}

void foo(FooConfiguration options) {  }

At a glance (and with IDE support), one can deduce that foo’s options have a required bar string, and a biz that defaults to 3. Where as a similar code in Ruby could look like this:

def foo(options):
  # …
  enc = options.bar.encoding #Oh, so `bar` is a string. Gotcha!
  # ... 
  (options.biz || 3).times { puts enc } # if `biz` is falsey, then `3`. Can I send a boolean? Oh, no, it `#.times` it, so it is a number. 🤕 
  # ...
end

Both these pain points were addressed, interestingly enough, by the same approach: Types.

In Crystal there is a very smart, very competent compiler that keeps track of the busy work of knowing what methods any reference might have, if it is nil or what can and can’t be done to an object. This was a godsend.

As luck would have it, I was also in close contact with some very vociferous advocate of Crystal. You can imagine, with the tagline: “Fast as C, Slick as Ruby”, I was hooked.

My breakout project, the moment I really “got” Crystal was when we needed some dashboard like solution. We realized we had built too many internal tools and no one place to cluster them all. For whatever reason we didn’t want some static lame webpage. We decided to roll out our own. Some YAML parsing, some macro goodness and a lot of type safety later we had it running. Adding apps was simple and a CI build made sure that all the things were correctly wired. It was a small project, probably way overengineered; but onboarding another Ruby developer was incredibly simple. That was a good selling point.

Why Crystal now?

That’s leading the subject, your honor!

Recently™ the nice people of Crystal announced the release of Crystal 1.0 - Production ready, stable and useful standard library all in a tight little bundle; so if it was ever the time to seriously look at Crystal; I would say today is the day. Other awesome features Type ergonomics

We talked about type safety, and some might have rolled your eyes expecting a very verbose way of writing the code; but let’s not forget one of the key drivers of Crystal is to have a similar syntax to Ruby. A dynamically typed language. So both:

class Foo                                                                  	 
  def biz
    puts "foo"
  end
end

class Bar
  def biz
    puts "bar"
  end
end

def invoke_biz(x)
  x.biz
end

invoke_biz(Foo.new) # => foo
invoke_biz(Bar.new) # => bar

Is both valid Ruby and Crystal. You can try that out in your browser in the crystal playground here! No type to be seen, thanks to the incredible type inference that comes with Crystal’s compiler. There is much cool things about it that I would need entire new blog posts to write, luckily Crystal’s book does a good job covering the basics: Type inference.

YAML / JSON / XML

It’s amazing how useful it is to have such a robust parsing library already in the standard library, no need to deal with extraneous dependencies to read a configuration file, or write one out.

require "yaml"

class Bar
  include YAML::Serializable
  property biz : Float64
end

class Foo
  include YAML::Serializable
  property foo : String
  property bar : Bar?
end

That’s all the code needed to parse something like

foo: hi!
bar:
  biz: 3.2

The same holds for JSON and XML. I find it baffling how, when reading and writing configuration is so easy, much more customization I allow when developing tools with Crystal.

De-centralized shards

Maybe as I’m getting older, I’m getting more paranoid about where my dependencies (which run, for all intents and purposes, arbitrary code that gets deployed on my behalf) are fetched; but I can’t/won’t wait for a cabal to approve a new dependency. Being able to choose either a local file or any git provider gives me a bit more peace of mind.

The logo… It’s alive, it’s moving, it’s alive, it’s alive!

The black logo in the Crystal’s home page is interactive. You can click and spin it around. At first glance, one can brush off that fact, thinking it is superfluous and useless; but I would urge you to wander: Would a bad language1 such a polished home page, such well written tutorials, such active community? 🤔

Where to now?

If you are interested, you can keep learning Crystal with the book and the online playground. Have a look at the awesome-crystal compiled list of things, some might inspire you in your current or next project. Reach out.


  1. reader’s interpretation of what a bad language might be. 

Melian

Random Posts

The essence of Event Sourcing

Event Sourcing is a somewhat trending topic, and you can find a lot of blog posts on what event sourcing supposedly is. I’ll throw my wrench in the works and try to explain what I see as “Event Sourcing”. #xpost

Dataloader, from the ground up

Ever heard of “dataloader”? From the simplest implementation to a batching and caching design pattern. Let’s dive into a brief tour of understanding this useful device.

#design

carbn

We are constantly trying to write code that easier for a human to comprehend. These high-level languages gave us the power of expression. And we like this!

We want more expression, and safety in the code we write; but we also want code that humans can understand and reason about. So what if we re-think our current software stack? And have humans evaluate other human’s code.

carbn is a Humans-as-a-Service, cutting edge technology that makes your code run in the distributed brains of our agents.

#raving

Limit the Problem Space!

Simply put, the “problem space” is the entire spectrum of inputs that exists in the process of finding a solution to a problem. How can we make our code less error-prone? In this post I explore some of the ways we can do so. Join me! #xpost