For the very rare circumstances where Ruby is too slow for what I need to do, there's usually code in some other language I can wrap in an extension, and that code is far more likely to be C or some other more established language than Crystal.
Ruby was my main language for scripting, but I switched to Crystal completely after getting comfortable with it. Now it is really hard to switch back to Ruby.
Also, I thing compiled language is the greatest friend for older programmers. Over the time I'm getting more and more sloppy and very often make some typing error. Crystal being compiled language saved me countless time by refusing to compile when I made the mistake. Meanwhile in Ruby it can only be found after the script ran for a while, sometime it became a disaster because the action was destructive.
As for the type checking, Ruby is getting more and more opt in type checking, so that reason is slowly diminishing as well.
If you go for Go or Rust you would never go for something like Crystal regardless, as they have different focuses. Just like Ruby, Crystal optimizes for code legibility and expressiveness, which both Go and Rust never really cared about, as it's about stability + performance (in the case of Golang) or safety + performance (in the case of Rust). Both of Go and Rust have horrible syntax compared to Ruby/Crystal, which is a fine tradeoff to make in cases.
Pseudo-code, but this is how I see the difference in expressiveness and syntax:
3.times {
println('Foobar')
}
for i in 0..3 {
println!("Foobar")
}
Both of them makes sense though, so not a huge difference, but it's the same way in lots of ways in Rust in general.> because it's a huge hindrance to trying a large number of languages out there with lots of nice things
I know exactly what you're talking about, as most of the time I work in Clojure, and trying to show Clojure to other programmers who are used to C-like languages and never heard of any lisp-like language is a constant struggle, as their first reaction is always "eww, parenthesizes everywhere!" even though their favorite language usually has the same amount or even more.
It's certainly a taste thing, but I am not enjoying it at all. I find it unpleasant. I think it's going to join the short list of "languages I can use but would prefer not to".
I've never learned Crystal, but if it's like Ruby, then I think I'd enjoy it just fine.
It retains most of the flexibility and DSL capabilities, but the type- and nil-safety allow you to leverage them without nasty surprises at runtime.
Not having to guess what a method or block returns and instead having the compiler tell you when you get it wrong makes an enormous difference.
To illustrate, when I started learning Go, I implemented Concurrent Hash Array Mapped Tries from some PhD thesis and the pseudo code algorithm actually looked pretty much like the Go code. A testament to how legible it is.
Then again, I have never tried ruby so I can't compare.
Overall I ended up writing more performance oriented applications and ruby (at least at the time) wasn't worth the 'it's pleasant to read and write' vs 'this ruby script that used to take a day now takes 15mins written in a language more suited for the task'. The other language I'd consider less expressive just in a less optimized-for-the-human-while-writing kind of way.
Crystal seemed nice in the respect that it might be the best of both worlds, but like mentioned elsewhere here it's still fairly niche.
Here's a simple example I'll do in JavaScript first since most people can read it. You have an array of the numbers 1 through 10 and want to get a new array with only the odd numbers.
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
arr.filter(num => num % 2)
> [1, 3, 5, 7, 9]
In Ruby you could express this the same way: arr.filter { |num| num % 2 }
=> [1, 3, 5, 7, 9]
But there's a better way. Since everything is an object, including integers, you can just ask the numbers if they are odd instead of using the mod operator. arr.filter { |num| num.odd? }
=> [1, 3, 5, 7, 9]
(Yes, method names can end in a question mark (?). By convention, methods that end in a question mark are predicate methods that return a boolean. Methods can also end in an exclamation point/bang (!). By convention, these bang methods are "dangerous" – they might do something destructive like mutate an object in-place instead of returning a new copy.)We can make this even more concise though.
arr.filter(&:odd?)
=> [1, 3, 5, 7, 9]
There's a bit to unpack here.:odd? is a Symbol
The & operator calls the #to_proc method on the symbol :odd? then converts the result into a block, which is passed to the filter method. In other words, the #odd? method is called on each element in the array no different than the previous example.
Here's another way to express the exact same thing:
arr.reject(&:even?)
=> [1, 3, 5, 7, 9]
This is just one simple example. Ruby can express many things on one line that would take several lines in other languages. This is what makes it expressive – it's the density and readability of operations.Another one of my favorite language features is that parentheses on method calls are optional, so a lot of syntax that looks like it would be language-level operators are actually just regular method calls to objects in Ruby. An example of this is ==, which is an operator in many other languages. In Ruby it is a method call. Consider this expression:
true == false
This is calling the #== instance method on the object `true` (which is an instance of TrueClass), passing `false` as the first argument. Written identically: true.==(false)
This is wild if you're coming from another language where things like this are operators, but it is completely natural in Ruby. It is extremely powerful.I started to use Crystal in a small commercial project, as a web backend, and it was very fun and promising.
But the project got cancelled for completely unrelated reasons.
I long for an excuse to use Crystal again. Hard typing combined with Ruby carefree style and EXEs without runtime... really cool stuff.
I think Crystal will crack the Win32 nut eventually.