Switch from Ruby to Crystal

Author: notinarkensas

Score: 72

Comments: 35

Date: 2020-11-06 07:33:52

Web Link

________________________________________________________________________________

robotmay wrote at 2020-11-06 08:33:19:

I'm a Rubyist by profession and I've tried Crystal multiple times but never really felt like I was enjoying it. I can't really put my finger on why, but perhaps it falls into the "uncanny valley" where it looks a lot like Ruby but is slightly different in peculiar ways.

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.

chrisseaton wrote at 2020-11-06 09:29:54:

The problem is:

> 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.

rishav_sharan wrote at 2020-11-06 11:40:40:

I have a love-hate relation with Crystal. Every few months, disenchanted with the core maintainers' priorities, lack of platform and tooling updates and overall deadland syndromes -

I denounce Crystal, promise I will never use it again, startup a golang or Rust project, make some non trivial toy stuff and then come crawling back to Crystal.

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.

rufugee wrote at 2020-11-06 13:57:48:

Go is simple. I can't, however, say I find go remotely as productive as I find ruby. In fact, literally nothing I've found is as productive as ruby. I've had high hopes that crystal would become a better, faster, more capable ruby, but I share your concerns.

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...

robertlagrant wrote at 2020-11-06 10:07:56:

Electric Vehicle Startup Nikola Motors is using Crystal to power their software in production

Not necessarily a ringing endorsement.

bnt wrote at 2020-11-06 11:15:31:

Well, not from a PR standpoint (and the author may not even be aware of what’s up with Nikola lately), but it’s used in production which is a huge positive.

yxhuvud wrote at 2020-11-06 11:58:21:

> Nikola .. production

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.

shaicoleman wrote at 2020-11-06 15:45:58:

The networking side of things still isn't mature.

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.

nurettin wrote at 2020-11-06 09:09:45:

So, what’s the catch?

No windows support. (Seems to be progressing, though)

https://github.com/crystal-lang/crystal/issues/5430

viraptor wrote at 2020-11-06 09:13:27:

You can track progress at

https://github.com/crystal-lang/crystal/issues/5430

But for many applications that's a non-issue.

rishav_sharan wrote at 2020-11-06 12:03:09:

The funny thing about the lack of "windows support" is that crystal is already good enough for windows programs which don't need networking (which is the only missing piece). Crystal can already work on Windows for use cases like gamedev (see crSFML), console apps, AI/ML etc.

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.

yeskia wrote at 2020-11-06 09:24:19:

Compilation times can hurt too, I'm not sure if that's improved or being worked on though.

progressbwc wrote at 2020-11-06 09:39:54:

Crystal does global type inference to be performant while “feeling like a dynamic language” - as claimed by the article. This leads to very high compilation times (which is non-linear so a program double the size will likely take more than double the time to compile) and last time I asked I was told it is not a solvable problem.

chrisseaton wrote at 2020-11-06 10:14:11:

> Crystal does global type inference

I think they stepped back from this a few years ago. It's not globally inferred anymore - you need to specify some types manually.

yxhuvud wrote at 2020-11-06 11:14:21:

Yes. The basic rule is that things that could be stored on the heap will need to be explicit or inferrable directly from the constructors. But as method parameters generally (except sometimes, like in procs) don't need typing it is quite possible to build full programs using only global inferrence. But that is usually not how programs are built - that would do away with things like objects and structs and other constructs that are extremely ideomatic.

viraptor wrote at 2020-11-06 09:47:33:

Does that overhead go down if you tag lots of places with types manually?

progressbwc wrote at 2020-11-06 10:01:49:

Yes but it is unlikely to make a difference unless a major part of ecosystem changes which is unlikely to happen as it will lose its “dynamic feeling”. Even the standard library has to be compiled when you compile your project.

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.

Kliment wrote at 2020-11-06 12:02:57:

Mildly offtopic but this is a thread most likely to have Crystal _and_ youtube-dl fans in it:

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

[3]

https://github.com/iv-org/invidious/issues/1411

[4]

https://github.com/iv-org/invidious/pull/1399

Rochus wrote at 2020-11-06 11:54:29:

Crystal is impressive work and the tools work seamlessly; it demonstrates how a statically compiled language can look when intelligent type inference is involved; wonder why a similar approach has not been taken by Python (well, Nim is a rather different language); the major point which concerns me is that the compiler get's rather slow when the codebase is large.

block_dagger wrote at 2020-11-06 09:15:41:

One of Ruby’s pillars is metaprogramming. Crystal lacks that entirely, making it a non-option for many Rubyists.

chrisseaton wrote at 2020-11-06 09:28:11:

Crystal has compile-time metaprogramming, which is it's own thing and has many sensible uses... but yes it's not the metaprogramming people are used to in Ruby and that the entire Ruby ecosystem is pretty fundamentally built on.

vasilakisfil wrote at 2020-11-06 09:44:52:

Crystal has macros which is the metaprogramming for static/compiled languages. And last time I checked, there are way more things that you can do with Crystal macros than Rust's macros. And crystal's macros feel very familiar to the language itself, are easy to understand and use them whereas Rust's macros..

lytigas wrote at 2020-11-06 10:54:16:

According to [1], Crystal Macros "receive AST nodes at compile-time and produce code that is pasted into a program." This is basically the same as Rust procedural macros[2]. You're probably thinking of "Macros by example"[3]

[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

vasilakisfil wrote at 2020-11-06 19:45:46:

Being able to receive AST in crystal is only good. However have a look here [1], 90% of your macros are covered with those helpers. In Rust doing the same things are either more difficult or impossible. For instance, I wanted in rust to inject allow/warn kind of derives based in env var. Nope.

[1]

https://crystal-lang.org/reference/syntax_and_semantics/macr...

viraptor wrote at 2020-11-06 11:53:49:

It's true the runtime meta programming is missing. Other comments mentioned some compile time options. But in my experience most Ruby doesn't really use metaprogramming for really hard cases. I've translated some larger modules to crystal recently and while there are magic things like activerecord out there, most metaprogramming is... simple. Often a glorified "I'd rather write a loop than repeat 3 similar method implementations". Outside of generic framework code I never ran into something meta which can't be trivially expanded to slightly repeating (and more readable) code.

ricardobeat wrote at 2020-11-06 11:12:19:

Which is fine since the language is not really aimed at replacing Ruby (despite this blog post's headline)

lipanski wrote at 2020-11-06 10:06:52:

There are a couple of points where Crystal really feels cleaner and somehow more expressive than Ruby.

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...

suzuki wrote at 2020-11-06 12:15:22:

I have written almost the identical Scheme interpreters in Ruby and Crystal: [1] and [2]. The biggest difference I have felt between them is the absence of good old Object, which can represent everything at runtime, from Crystal. I had to declare Obj and Val:

  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

tmstieff wrote at 2020-11-06 09:26:53:

Anyone have production experience with Crystal? I know it's not 1.0 yet, but there are some fairly mature web frameworks available that make the language look pretty attractive.

gdotdesign wrote at 2020-11-06 10:40:03:

I've build the backend of Base API, and the Mint programming language in it.

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

WJW wrote at 2020-11-06 11:16:42:

At my previous job there was a need for a microservice that would compute a PhotoDNA hash of images and then compare that to a list of "known bad" material. Since this was very CPU-intensive we opted for Crystal instead of the usual Ruby that was used for almost everything else there. When I left it was churning away at ~500 images per second (over many cores, obv).

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.

rishav_sharan wrote at 2020-11-06 11:59:23:

not production yet, but if you are looking to make simple apis, you can just go with the stdlib. Like golang, the Crystal http stdlib module is good enough for use in your project. Here is my half done repo where I have used it for a repo

https://github.com/rishavs/noir

, if you are looking for a reference.

The major things you might struggle with;

* Lack of IDE support. The DevEx is not that great compared to the other languages.

ricardobeat wrote at 2020-11-06 11:11:23:

Seeing as this is published in the LogRocket blog, is it being used by the company? Would be great to hear more about experiences in production.

Kkoala wrote at 2020-11-06 11:19:14:

Probably not, they have like hundreds of thousands similar articles, which they use as marketing and SEO for their product AKA content marketing.

BilalBudhani wrote at 2020-11-06 11:25:49:

I tried Crystal at the start of this year for an experiment and found the language is still in its infancy stage. Thou there are good things happening in their community but still a long road towards becoming a serious contender for switching.

Having said that, Crystal can definitely be considered for building micro-services to delegate more resource intensive tasks.