Quirks and differences to Ruby

Despite the similarities, there are substantial differences to Ruby, here we will highlight the most obvious and interesting ones.

Types, type checking and type unions

The most apparent difference is that Crystal uses and mostly enforces types for variables. It has great type inference — if you do not explicitly define the type of a variable the compiler figures it out itself.

The way this language does typing is a sort of mix between static and dynamic typing. It allows you to change a variable’s type

but it also allows you to enforce a variable’s type

Types Unions

Were you wondering what the (Int32 | String) type was in the error message above?

This is a so-called type union, which is a set of multiple types.

If we were to enforce a to be a union of Int32 and String , the compiler would allow us to assign either type to that variable as it knows to expect both.

Type Inference and Type Checking

The compiler can figure out the type of a variable himself in most cases. The type inference algorithm is specifically built to work when the type of the variable is obvious to a human reader and does not dig too deep into figuring out the specific type.

In the cases where multiple conditions are plausible, the compiler puts a union type on the variable. Crystal code won’t compile if the possible types do not support a given method invoked on them.

Does not compile!

This is the way the compiler protects you from silly mistakes with mismatched types, something that is really common in dynamic languages. Its like having your very own programming assistant!

The compiler is smart enough to figure out when a variable is obviously from a given type

There are ways to ensure the compiler that the appropriate type is set.

puts a.as(String).camelcase

This checks that the a variable is a string and if it is not, it throws an error.

Enforcing types

As we said, we have the option to enforce a variable’s type or let it be whatever.

This holds true for a method’s parameters as well.

It is usually good practice to not enforce a variable, as it leads to more generic code.

Concurrency

Its concurrent model is inspired by that of Go, namely CSP (Communication Sequential Processing).

It uses lightweight threads (called fibers) whose execution is managed by the runtime scheduler, not the operating system. Communication between said threads is done through channels, which can either be buffered or unbuffered.

A lot of fibers who communicate between each other through channels

Crystal currently runs in a single thread but their roadmap intends to implement multithreading. This means that it currently has no support for parallelism (except for process forking), but that is subject to change.

Because at this moment there’s only a single thread executing your code, accessing and modifying a variable in different fibers will work just fine. However, once multiple threads is introduced in the language, it might break. That’s why the recommended mechanism to communicate data is through channels.

Metaprogramming

Crystal has good support for metaprogramming through macros. A macro is something that pastes code into the file during compilation.

Let’s define our own version of Ruby’s attr_writer

Calling attr_writer(foo, Int32) will evaluate to





end def foo(foo : Int32) @foo = fooend

Crystal macros support iteration and conditionals and can access constants.

Miscellaneous

Crystal has taken a lot of cool features from other languages and provides various syntax sugar that is oh so sweet!

Initializing class instance variables directly in a method

def initialize(@name, @age, @gender, @nationality)

is equal to

def initialize(name, age, gender, nationality)

@name = name

@age = age

@gender = gender

@nationality = nationality

end









end def initialize(name, age, gender, nationality) @name = name @age = age @gender = gender @nationality = nationalityend

Implicit object notation

Switch statements support invoking methods on the giving object without repeatedly specifying its name.

External keyword arguments

My personal favorite — Crystal allows you to name a function’s parameters one way for the outside world and one way for the method’s body

Hooray, readability!

Compiler

As you saw earlier, this language is compiled to an executable. Regardless, it still has something like a REPL which proves to be similar to our beloved irb — https://github.com/crystal-community/icr

You can also directly run a file without having to compile it and then run it, via the crystal command.

> enether$ crystal AA_Tree.cr 200000

Time it took: 0.536102 seconds.

This runs a little bit slower because we do not make use of the optimizations that the - release build flags brings with itself.

C Bindings

There is a way to write a performant library in Crystal which you can run in your Ruby code. The way you do this is to bind Crystal to C, which allows you to use it from Ruby.

I did not delve too deep into this but apparently it is easy and you can do it without writing a single line of C. That is awesome!

Conclusion

If you write Ruby, picking up Crystal is natural and can quickly find yourself writing performance-critical software in it. I believe it has a lot of potential and can yield a lot of benefits to our community but also to non-ruby programmers, as the syntax is just too easy to pass up. It is a joy to write and it runs blazingly fast, that is an unique combination which very few languages can boast with.

I hope I’ve sparked your interest by these short examples! I strongly encourage you to take a look at the language for yourself and notify me if I’ve missed something.

Here are some resources to read further up on:

Google Group

Gitter Chat

IRC

Subreddit

Newsletter