Switch from Ruby to Crystal
blog.logrocket.com
blog.logrocket.com
I vaguely recall being confused about including other files and module definitions last time, like you could put the requires in really weird places. It has been a while, though.
Oddly, I found Elixir much more enjoyable, and extremely quick to learn. Whereas Crystal feels like an attempt to build a "compiled Ruby", Elixir feels more like it took the ideas behind what Ruby is and applied it to a wildly different design, and as a result actually feels like how Ruby felt when I first learnt it.
But I do give Crystal a go every once in a while just to see if I finally get the hang of it.
> Crystal’s syntax is almost identical to Ruby’s...
...But the semantics are very different.
So you're writing in one language but it's as if the meaning has been swapped out underneath for the meaning from another language.
I hate the fact that I love this language and the fact that nothing else (except perhaps f# and nim) seems to have the same effect on me.
My favorites Crystal features are its do-end syntax, dat sexy type system hnnnnghhh... , domain modelling using sum types, null checking using types, ultra simple OOPs, insanely productive std and its sheer performance.
The bits about the language I detest are the overuse of macros, the utter lack of any platform (windows, http/2 etc.) or tooling improvements (IDE support, slow compilation). The core maintainers are amazing but have a weird obsession with just refactoring the language semantics. Things which really would matter for any language usage are just relegated to GH issues which haven't had any comments on it for months (if not years).
The lack of a BDFL and corporate sponsorship really hurts Crystal bad. Its a language without directed growth. For a language which seems to be used the most for web servers, it lacks http/2, db pipelining and async db drivers. There have been plans for redoing the http module for years but it hasn't been done yet.
Yet, I love this language despite all its shortcomings. I find it to be one of the most readable languages out there. The community is full of amazing individuals who are ever helpful and welcoming. The core maintainers are super talented developers who really value quality of code.
Right now I am working on an api servers and have 3 early implementations - one in Rust-actix, one in pure crystal (no framework) and one in Go-Fiber. The joy of using Crystal and feeling like I am in control of the project are reasons which are pushing me towards using Crystal for the project. But I know that by the next few years, we likely still wouldn't have http/2, async drivers, nice IDE support and many other features that I really need.
And so, I will likely have to go with Go. (Rust syntax is just too complex for my taste. And I am not talking about the celebrated borrow checker).
I like go.
Go is simple.
Go is Productive.
Go is... just not Crystal.
I left ruby and rails years ago, and here I am in 2020 thinking about going back. I've made very strong attempts to use python, go, kotlin and groovy in the years since, but literally nothing touches ruby in terms of sheer developer productivity (at least for me...ymmv). I suppose I'll look at elixir next...
Not necessarily a ringing endorsement.
Well, that is the crust of the issue. Nikola has not really shown that they are not vaporware, and hence there really is no production.
Invidious needs your help!
invidious[1] is an open alternative frontend to youtube with low bullshit, that works without js. It's written in Crystal. The core developer decided it was too much for them and has taken a (likely permanent) break from the project[2], and the community is struggling to maintain it[3] because there's very few people who have ever seen or heard of Crystal and like youtube-dl it's subject to breakage whenever google tweaks something on the youtube page. If you are a Crystal person and this is of interest to you, please help. For a bit more information on the way the code is structured and the current work that's happening, see [4]
[1] https://github.com/iv-org/invidious
[2] https://github.com/iv-org/invidious/issues/1320
A couple of years ago, I tried to write a parallel download app as my first Crystal app. It ended up as slow as the Ruby version. After some digging, I found out it was because the DNS resolver wasn't multithreaded.
Years later, the issue is still open.
https://github.com/crystal-lang/crystal/pull/2829
https://github.com/crystal-lang/crystal/pull/4236
It's still a good fit for CPU-bound workloads, and efficient background jobs and where I wouldn't want to install Ruby. Also seems be a good fit for AWS Lambda.
No windows support. (Seems to be progressing, though)
But for many applications that's a non-issue.
There is some caching to help but times are still high. IIRC the compiler is not parallel either, so having more cores won't help.
There is also a —release compile flag that you would want to use for production and it is much slower.
I think they stepped back from this a few years ago. It's not globally inferred anymore - you need to specify some types manually.
Yet the Crystal team hasn't made a preview windows build which can enable all those use cases and the act of using Crystal on Windows is super convoluted as a result.
[1] https://crystal-lang.org/reference/syntax_and_semantics/macr...
[2] https://doc.rust-lang.org/reference/procedural-macros.html
[3] https://doc.rust-lang.org/reference/macros-by-example.html
[1] https://crystal-lang.org/reference/syntax_and_semantics/macr...
The authors of the language avoided aliases (no more size vs. length or inject vs. reduce discussions) and generally there's only one way to do things (Strings are always wrapped in double quotes).
Crystal has abstract classes/modules/methods, generics and method overloading, three powerful techniques which are missing in Ruby entirely and which can be very helpful in some cases.
You can mark global methods as private and you can also mark classes as private. The latter can prevent users of your "shard" (gem) from accessing such classes, pretty much like how Rust modules can be marked for external use or not.
Constructor arguments can be turned into instance variables automatically, if you add `@` to the argument names.
Marking a construct as private happens inline with the definition, unlike in Ruby where it functions as a divider. This makes your code a bit easier to read and you can group methods in any way you like.
I find the built-in JSON, YAML and XML parsers to be very elegant. For the common use cases, you just have to define normal classes with attributes and include the JSON::Serializable module - this will also traverse attributes in search of serializable types. You can also do more complex transformations with annotations - have a look at https://crystal-lang.org/api/0.35.1/JSON/Serializable.html - but writing an HTTP client for some random API in Crystal feels very natural. On top of that, the built-in HTTP client is more than decent (which I can't say about Net:HTTP).
Generally the standard library is packed with goodies and, though the ecosystem is scarce in some areas, it's usually easy to replace and you can get away with less dependencies.
The inferred static typing which allows for union types is a neat choice - it doesn't force you to write types (most of the times) but it does give you some guarantees. I personally prefer to explicitly name my types, especially in public method definitions, because it helps document the code without having to write anything on top, but it's up to you.
To be honest I'm not really missing the metaprogramming aspect. In large Ruby projects this can easily turn into a mess and it makes searching through your codebase a horrid experience. Crystal does have macros but they have some limitations (they operate at compile-time).
Performance is just amazing and you can also build static binaries.
If you like Ruby, I'd really encourage you to give it a try, at least as an experiment to an "alternative Ruby".
To conclude, here are some code samples (shameless plug, but you can also browse https://crystalshards.xyz/ for other projects):
* https://github.com/defense-cr/defense/blob/master/src/defens...
* https://github.com/defense-cr/defense/blob/master/src/defens...
* https://github.com/lipanski/kilometric/blob/master/src/kilom...
class Obj
end
# Value operated by Scheme
alias Val = Nil | Obj | Bool | String | Int32 | Float64 | BigInt
to define Cons Cell of Scheme: # Cons cell
class Cell < Obj
include Enumerable(Val)
getter car : Val # Head part of the cell
property cdr : Val # Tail part of the cell
def initialize(@car : Val, @cdr : Val)
end
...
end # Cell
Note that you see generics, Enumerable(Val), and constructor arguments with '@' in the excerpt above.As for performance, Crystal is faster than Ruby 8.6 times as interpreter and 39.4 times as compiler [3]. You can use Crystal as a superfast (and typed) Ruby interpreter, in a sense.
[1] https://github.com/nukata/little-scheme-in-ruby [2] https://github.com/nukata/little-scheme-in-crystal [3] https://github.com/nukata/little-scheme#performance
Base API runs on Heroku and it's memory consumption is really low (around 20Mb on average) also it's slug size is 3.3MB.
I think it's a really good language, the syntax is way more clearer then any of the other similar languages (Go, Rust, Nim).
It has two problems currently as far as I can tell: - Windows support - when it hits there is no reason for me to use anything else, I'll be able to write desktop applications in it with a Webview or CLI apps - Lack of mature libraries - in time I think this will remedy itself
Base API: www.base-api.io Mint: www.mint-lang.com
Development was pretty smooth and it was pretty easy to get new people onboarded since it look so much like Ruby. Some caveats though:
- If you want every last bit of performance, type signatures are very needed. Otherwise the compiler will still have to determine at runtime if the argument to a function is a uint32 or a uint64 for example.
- Library support is definitely lacking compared to Ruby. For example, AWS has an official SDK for Ruby with support for everything you can think of. Crystal has a 3rd party library with support for a few services. It's just not comparable.
The major things you might struggle with;
* Lack of IDE support. The DevEx is not that great compared to the other languages. * Long compilation times. Specially if you add more libs to your project. * http/2, postgres pipelining and async drivers. Not a big deal as the Crystal drivers are performant enough. * lack of community packages. Most stuff will have to be done by hand.
Having said that, Crystal can definitely be considered for building micro-services to delegate more resource intensive tasks.