There is a vast space for “modern non-system compiled languages”. It is a space where most of the new languages rise (and die), and in which I am constantly attracted.
Even if I am quite comfortable with Rust (and Rust did remarkable improvements in ergonomic1), I always have the feeling that I am driving a super expensive and powerful sport car just to go buy groceries down the road.
For this reason, I always keep an eye open for a language in the sweet spot between a Python script and Rust.
In particular, I look for languages with:
- A sound type system designed by people who did not sleep under a rock in the last 30 year. In particular, if types are nullable, it is a big nope.
- A set of standard tooling. In particularly an opinionated dependency management.
- Easy concurrency (e.g., via green threads or whatever).
But let’s stop wandering around. Back to the main topic.
On March 22nd, 2021, I read about the release of Crystal 1.0. I already talked about Crystal some years ago, and I said that we needed to keep an eye on it.
If you do not know it, let’s just recap. Crystal is a general-purpose, object-oriented programming language whose syntax is inspired by Ruby. It is statically typed, and it is compiled into native machine code (using LLVM).
So, since it abides to all my requirements, why don’t we take a more in-depth look?
Crystal: The Good
Basically, a Compiled Ruby
Crystal as a really advanced type inference. Even if it is statically typed, it looks like plain old Ruby. For most of my exploration I forgot to even add types. Crystal can almost always infer the right type (or the right union type) and behave accordingly.
Crystal’s Standard Library is also very similar to Ruby’s one. As a consequence, you can copy Ruby snippets and paste them into your Crystal application, and they may work (with minor adjustments).
However, it doesn’t have the same pitfalls. For instance, types cannot be nillable. If a variable can be nil
it must say it (e.g., declaring its type with the union type String | Nil
).
Easy Concurrency
Crystal has a nice convenient threading mechanism based on fibers. A fiber is a lightweight thread managed by Crystal runtime (i.e., what we usually call green threads).
To make a comparison with a better known language, that is the same mechanism of Go’s goroutines.
|
|
You use spawn
to spawn a new green thread and if you want to communicate between threads you use channels.
|
|
Pretty standard, but powerful, stuff.
v1.0.0
The fact that Crystal is finally 1.0 means that is finally a viable choice for something more serious. After years of stabilization, we have a language interface that does not change every two months.
Compared to other languages we will discuss in the future, this is a big pro.
Crystal: The Goodish
Decent Tooling
Among the (only two) good things that Go popularized among developers there is integrated tooling. It is impossible for a modern language to not have built-in tools such as formatters, test runners, benchmarks, packaging and more.
Crystal keeps this promise. Out of the box, Crystal includes a test runner (they are called specs
in the official documentation), a code formatter and some other tools useful for IDEs and plugins (for instance, crystal tool hierarchy
that shows the hierarchy of all types in a script2).
At the end they do their job, but there is still work to do in this compartment. Other languages/runtimes, such as Deno, have way more stuff to offer.
Very Powerful Macro
Macro are goods if you are willing to seldom use them. They can really obfuscate the code and makes things weird.
But if you really must, Crystal’s macro are very powerful. It can literally manipulate the AST at compile-time.
|
|
For instance, in this example, the macro write different code depending on the parameter of the macro invocation. And there is much more.
You know the deal: With great power comes great responsibility. So, this is a good thing. If in good hands.
“Killer” Frameworks
There are already two interesting web frameworks: Amber and Lucky. This kind of languages (especially one inspired by Ruby) thrive or die depending on their web frameworks.
After a quick look to both, they seem pretty complete and powerful (Amber is explicitly inspired by Ruby on Rails).
On the other hand, Crystal adoption is still pretty low. A quick search on GitHub shows me only 97 repositories with more than 100 stars (771 with more than 10 stars). It’s something, but there is still a lot of road to walk.
How much Amber or Lucky will gain traction (and how much of this traction will propagate to the language ecosystem) is still uncertain, but they will help.
Crystal: The Baddish
The usual disclaimer. I just want to clarify that I am talking about my personal preferences. These are universal language flaws, they are just friction points with my way of programming.
Basically, a Compiled Ruby
This is not an error. I really put the same good point as a badish point. The fact is that I can hardly fit with Ruby’s syntax. It is mostly alien to me and, therefore, jumping into Crystal will give me an extra challenge.
There are people that love Ruby’s syntax. Unfortunately, I am not one of them.
Compile Time
To compile the little concurrency code example above, Crystal took 1.2 seconds.
|
|
In general, trying with bigger projects, I can say that there is still work to do on compilation time. For what I need to do usually, compile times are not a dealbreaker. But I know that for someone they are.
Windows is not natively supported
That’s a serious point. I know Crystal developers are working on it and that they did not want to delay 1.0 further to wait for Windows. Yet, I already know what happen when languages do not offer Windows support soon enough.
Windows is still a widespread desktop SO. A language without Windows support does not encourage adoption (even if you are Apple and you are called Swift, you still became a pretty purpose-specific language).
I hope Windows support will not be far away. Crystal needs it to grow.
Conclusions
Crystal was, and it is, an interesting language. Just to recap:
Pro:
- It will be very familiar to Ruby developers.
- Statically type checked and very good type inference.
- Concurrency via green threads (à la Go)
- FFI with C by design.
- Good performances.
- No
nulls
as every good modern boys and girls. - Mighty macros.
Cons:
- Tooling and IDE support still very, very young.
- Mighty macros (yep).
- Compilation time could be better.
- Still no native Windows support.
At the end, as a personal consideration, I would say that I am not in love with it. There was no spark. At least for now. It is a good language but still not good enough to make me drop what I use and to go deep in it.
And what do you think? Have you tried it? It is the best thing ever, and I missed that? Do you love it? Do you hate it?
In the meanwhile my exploration will continue. Let’s just say it will continue with a sharp change of direction.
Photo by Sharon McCutcheon on Unsplash