If you were to ask any developer whom I have frequent contact with what my favorite language is I can guarantee you that they would all shout out a resounding “Crystal!”. The reason for that is simple. Whether it be Twitter, Telegram, Reddit, or blogs I am always sharing my love for the Crystal language. Hell here I am doing it again.

Now, my language of choice has been well established, but it wasn’t always Crystal. I’ve been in web and software development for 8 years and Crystal is still a fairly new language (not even out of beta yet). For the last couple years I have been a heavy JavaScript developer, but I’ve also worked with Rust, D, C++, Elixir, Nim, and several others. So why does Crystal take the spot as my favorite even though it’s still in development?

Well to answer that it helps if you understand, or at least have seen, the Ruby syntax. I know it’s a controversial topic, but I actually find the Ruby syntax extremely beautiful. I mean take a look at this code and tell me that you don’t just fall in love a little.

class SimpleClass

def say_hello(name)

puts "Hello, #{name}"

end

end simple_class = SimpleClass.new

simple_class.say_hello("World") # => Hello, World

Crystal shares the same basic syntax as Ruby which is the first reason I love it so much. As a matter of fact, that Ruby code above is also perfectly valid Crystal code. Mind blown yet? No? Well let’s continue then.

Crystal is compiled

This is the first huge difference between Crystal and Ruby. Crystal is a compiled language, which means that many errors are caught at compile time before they can cause problems in production. Of course this has a small “speed of development” tradeoff, but it pays off in the long run. It also means that you can build and ship a binary rather than having to deploy your entire code repository. This is very good for people that might have a VPS with only 10 or 20 GB of storage space.

Of course compilation does come with it’s drawbacks as well. Interpreted languages, are extremely fast to write and test because you don’t have to wait for your code to compile every time you make a change. Ruby is also a very advanced language when it comes to meta-programming, or writing code that writes and modifies code. This means what with Ruby code can be changed in the fly, methods can be generated based on data that is constantly changing, etc. Such is not the case with Crystal, mainly for safety reasons. When a compiled language changes itself during run time it loses a lot of the safety features that make a compiled language great.

Crystal is statically typed

This is one of Crystal’s best features. Most all languages have some kind of basic type system. Even JavaScript has the basic types Number, String, Object, Array, etc. Many languages, however, don’t enforce types or provide any kind of type checking. JavaScript, Ruby, Python, and PHP are this way (I know I’ll get comments about Python and PHP actually having types, but they really don’t. At least not to the same extent as real typed languages).

Crystal is able to infer types a lot of the time, that’s why this code

def str_concat(str1, str1)

str1 + str2

end puts str_concat("Hello, ", "World")

works even though it doesn’t have any type information. Of course this code will also work for integers or any other values that can be added together using the + operator. For that reason we can easily add type definitions to our code.

def str_concat(str1 : String, str1 : String)

str1 + str2

end puts str_concat("Hello, ", "World")

puts str_concat(1, 5) # => Throws an exception

Now the str_concat method only works with strings.

Crystal also supports union types. These are basically combo types, meaning that something can be either type A, or type B. Here is an example of that.

alias Num = Int32 | Int64 | Float64 def sum(num1 : Num, num2 : Num)

num1 + num2

end puts sum(3, 4) # => 7_i32

puts sum(4, 9.25) # => 13.25_f64

puts sum(7, "eight") # => Exception

Crystal has macros

Remember how I mentioned meta-programming earlier? Well Crystal actually does allow that as well, but only at compile time. Crystal does this through special methods called macros. Macros can be a confusing topic for some, but with a little bit of knowledge about how programming languages work I think they can be understood fairly easily.

Most programming languages have something called a tokenizer (or lexer), and a parser. The tokenizer is responsible for taking the raw textual representation of your code and transforming it into “tokens”. These tokens represent everything from strings to identifiers (def, class, module, etc.), to numbers and usually include basic information such as the line and column where the token was found. The parser then takes those tokens and transforms them into an AST (abstract syntax tree). This tree contains nodes that represent classes, modules, methods, etc.

This is an extremely basic, and probably somewhat flawed definition, but it works for our purposes. A macro gets expanded during the AST creation phase and actually modifies the AST as it is being created. That means you can add methods dynamically (at compile time), create and modify classes, etc. Crystal’s macro system is pretty basic when compared to the likes of Nim and Rust (with it’s extremely confusing macros), but it is helpful nonetheless. Here is an example of a simple macro.

# This is an anti-pattern in Crystal, but I do it anyway sometimes macro alias_method(new_name, existing_method)

def {{ new_name.id }}(*args, **kwargs)

{{ existing_method.id }}(*args, **kwargs)

end

end def send_tweet(message)

# Do something

end

alias_method :tweet, :send_tweet tweet("Hello, world")

Basically the alias_method macro just takes a new method name and the name of an existing method and creates a method that replicates the existing method. Ruby can do this with alias but since it’s considered an anti-pattern the Crystal devs didn’t include it into the language itself.

Crystal is fast

As you can see, Crystal already has a lot going for it. It’s beautiful, compiled, statically typed, has meta-programming capabilities, what could possibly be next? Well what if I told you that Crystal is also almost as fast as C?

I know it sounds unbelievable. I mean beautiful languages can’t be fast right? There are always trade-offs in the world of development. It is true though. At the very least Crystal is easily 100x (not joking) faster than Ruby. Just take a look at the benchmarks.

Speed isn’t everything of course, but it sure is nice to not have to worry about it.

What don’t I like?

There are a few downsides to using Crystal, especially right now while it’s still in development. The first one is that Windows compatibility is basically non-existent. You can download and run Crystal in WSL (Windows Subsystem for Linux), but that’s not as good as native compatibility. Now this is being worked on, but it hasn’t been a top priority so development has been slow.

Since it’s newer the development tools really aren’t all there yet. There has been great progress on a Crystal language server called Scry, but once again a lack of full time devs leads to projects like this taking a very long time to complete. There are also plugins for Atom and VSCode which are in development.

Lastly is a problem with communication. This has been addressed several times, and to their credit the core devs have been doing a better job in recent months, but communication has always been a little bit of an issue with the Crystal project. Crystal is being developed and funded by a company called Manas Tech out of Argentina. They aren’t a huge company like Google or Microsoft and as such can’t pour hundreds of thousands of dollars into development. As such much of the time spent developing Crystal has been graciously donated by Manas to the community, and sometimes other projects take priority.

Point is, Crystal is a wonderful language and you should definitely give it a shot. I will continue to evangelize and hope others will pick up the torch and do the same. If you want you can visit my GitHub repo and check out some of my projects (many of which are Crystal ones surprise, surprise).

Happy coding!