Crystal-Lang – Beauty of Ruby, Faster Than Go
crystal-lang.org
crystal-lang.org
I'd like to see more things like this on HN.
But...the fact that it's been bootstrapped means that getting it up and running on Windows will be a bit more of a challenge than if it were written in something that can just be built on Windows (C? Go?). I did find this:
https://github.com/manastech/crystal/issues/26
...but the effort seems to have stalled.
Cross-compilation would fix the bootstrapping problem, and is also important for its own sake; for building code that runs on ARM, for instance (for Android, iOS, etc.).
Looks like neither Windows support or cross-compilation have any advocates on the core team, which is sad.
http://crystal-lang.org/docs/syntax_and_semantics/cross-comp...
But porting stuff to Windows is far from trivial: there's making LLVM work, there's exception handling, string handling (seems it's UTF-16 rather than UTF-8 in some places), etc. It's definitely on our long-term roadmap, it will just take a lot of time and effort.
And ARM is also on our roadmap, hopefully a short/mid term one.
LLVM is supported on Windows. [1]
Talking to Windows APIs requires UTF-16, but UTF-8 can (and should!) be used in all languages on all platforms internally. It's trivial to add a UTF-8-to-16 conversion step when calling a Windows API (and the other direction on receiving such strings from Windows).
What puts me off somewhat is that it seems that Crystal uses the same stupid scoping rules as Ruby. I would not call them insane, but they are kinda funny (compared to normal lexical scoping like in ML or Scheme), and the new language could fix that. [The documentation does not have a section on scoping, so I'm not 100% sure about this issue]. Another strange element of the Ruby syntax that does not make a lot of sense to me is proc.call("blah"). Was that "call" bit really necessary in the new language? Probably, not really.
In any case, my complaints are quite minor, and probably the author considered all pros and contras when making these particular choices. Overall the work looks very interesting. I usually prefer OCaml for my programming, however it's not always a perfect tool and I was trying to find some other language that will work better. So far, nothing could work better, but Crystal is a step in the right direction, I think.
I agree with you about the scope stuff though. I think I even remember Matz lamenting it at one point and saying it may change in a later version. That was back at 1.8 though, so I guess at this point it's not going anywhere.
Actually, because it's a cool project, we've bent the rules twice for it already. The last time it was posted, it had had 65 points just a few weeks earlier: https://news.ycombinator.com/item?id=9467935, and a major thread a few months before that: https://news.ycombinator.com/item?id=8658358.
Doesn't this defeat a large advantage that static typing has over dynamic typing -- catching when something is set incorrectly?
In short: there's no "undefined method" errors at runtime in Crystal, so you have the same guarantees that a statically type checked language gives you, plus the ability to program in a duck-type oriented way.
Is there any documentation to back up the "Faster than Go" statement? I compiled and tested the HTTP server example, and pounded on it with ab. I don't have enough time tonight do an in depth comparison, but it doesn't seem much faster than Go.
On the plus side: the binaries are nice and small, and the memory usage is low.
https://github.com/kostya/benchmarks
As always, take benchmarks with a graint of salt. We use benchmarks mostly to know how far away we are from C (1x, 10x, 100x, etc.), so any language that has a speed "in the order of" C is good enough for us, small differences don't matter much.
http://crystal-lang.org/docs/using_the_compiler/index.html
Crystal is faster than Go in some cases (mostly because we use LLVM and it optimizes really well), but right now Go has faster context switches between goroutines (in our case fibers) and in general faster concurrency (for example it distributes the goroutines across multiple threads, we are using one for now). But improving these is definitely on our roadmap and should be doable.
More good news is that we are using Boehm GC and didn't spend a lot of time optimizing on our side (rather than just relying on LLVM), so a custom GC and more optimizations (like maybe escape analysis) could improve performance even more.
That said, it looks like a really cool project, with many nice language features Go is clearly missing. Attacking Go on performance is a bit silly though, this is purely by the choice of being a frontend for LLVM, not by language design. The choice for LLVM is understandable, and at first I thought Go made a mistake here, LLVM is really nice - but it also clearly has it's downsides. On one hand I wish Go had access to the optimization power of LLVM, on the other hand I'm glad it has no dependency on it and is standalone. Thanks to this, cross compiling is a breeze, and adding support for new platforms is entirely controlled by the Go team and it's contributors, although I don't know how they're going to handle iOS support now that Apple switched to LLVM bytecode for application distribution which they then compile to native code on their servers before delivering to the device. Maybe Go can add an LLVM bytecode target, which would mean it could also benefit from it's optimisations.
But in most cases, once you've passed a certain treshold, performance is not that important anymore, and once you're running reasonably fast native code, that should not be a problem anymore. I'm already happy with the current trend of going back to native code instead of thinking that CPU's are fast enough to interpret everything and that memory is cheap.
I also quickly tested compiling a 'hello world' example with both Go and Crystal, and the binary sizes were quite similar:
- Go (1.4.2): 620K
- Crystal: 619K
- Crystal release: 528K
So Crystal is a little bit more compact at first glance, but at first glance not that spectacular. Go is notorious for generating quite big binaries. The thing is however, that Go always produces static binaries - it does everything with it's own stdlib, unless it involves DNS resolving. A quick LDD on the crystal binaries revealed it wasn't a static binary and depended on libc. So I tried compiling the same hello world staticly in crystal, which gave me some warnings and a whole different picture: a whopping 1.8M binary. So more compact? Not necessarily.
Library-wise however, Go is lightyears ahead of Crystal. Go's stdlib is amazing, and there are an surprising amount of 3rd party libraries written in native go. Crystal on the other hand has the advantage of being able to use existing C libraries with ease and little overhead, but this could also be a downside and impact clean library design and consistency in the long run. It not being 100% Ruby compatible and not able to blindly use any Ruby library is a handicap here.
All in all, I think both Go and Crystal could learn from each-other. Go is a bit more mature and has clearly been accepted in the server/sysops space, many new projects there being written in it (docker, consul, etcd, heka, kubernetes, prometheus, influxdb, the grafana backend, ...). For crystal to earn it's place somewhere, it will need a lot of work.
Yes, Crystal will definitely need a lot of work. On the other hand, maybe this work is also fun :-)
Also there's the thing that Go has big Google behind it, so of course it's much more advanced (plus they also started earlier).
[1] http://crystal-lang.org/2013/11/14/good-bye-ruby-thursday.ht...
"Crystal is not interpreted. It doesn’t have a virtual machine. The code is compiled to native machine code by using LLVM."
http://crystal-lang.org/2013/09/04/happy-birthday-crystal.ht...