The Crystal programming language has been mentioned a couple of times on this forum (by asterite, its original creator, almost two years ago), but I think it would be useful to have more discussion.
I recently came across it again while looking at these benchmarks, where Nim's json parsing performs much better against PyPy and Node than in my own benchmarks, but Crystal comes out on top. Crystal also surpasses Nim in Havlak loop finder and base64 benchmarks.
I find this surprising, given that Nim seems to be further along in the development process, and their policy of "never have to specify the type of a variable or method argument". What is Crystal doing to be faster?
I personally prefer Nim: I like Python's syntax better than Ruby's, and Nim now has a better license. I think Nim has a better chance of breaking the ceiling of new programming languages and becoming mainstream, perhaps even dominant in certain fields someday. But performance is a crucial selling point for Nim - if it's not "faster enough" then people won't make the switch from something like Python, or they'll continue using another systems language for the small fraction of their code that requires optimization.
The benchmarks being by one of the top Crystal contributors might have something to do with it. ;)
I took a short look and fixed a few minor things in these benchmarks already. You could improve Nim's performance by implementing better algorithms for these problems. For example create a lookup table for base64, as the C implementation does, but then your binaries are a bit bigger. Or do a streaming parse for the json benchmark, as the Crystal implementation does. (I have the code for it, but want to clean it up and eventually add streaming json parsing to the Nim standard library)
While I enjoy optimizing benchmarks, I don't think they're as useful as some people believe. If performance really matters to you in Nim, you can disable runtime checks, you can manually manage memory instead of using the GC, you can write your own optimizations in the form of term rewriting macros, and you can go down to intrinsics or assembler if it's really necessary.
I don't think benchmarks matter much. Too many things are mixed: the data structures used, the algorithms for some of the operations, the backends (gcc, llvm), etc. In some other benchmarks out there Nim wins. It does certainly win in the brainfuck interpreter. Also, Nim usually is shorter to write (probably because of the lack of "end").
What's important for me is (in no order): 1. code clarity, 2. good-enough performance in real applications. I think both Crystal and Nim can offer this.
If you check the JSON benchmark, Nim takes just a bit longer than Crystal. I'm sure there's a small optimization in Nim's JSON parsing that could be introduced, but that performance is already good-enough for real applications so it's not that important.
About the "never have to specify types": even if you don't specify them, the compiler still types things to the most narrow type (in most cases), so it's similar to Nim in that things have types and they can be optimized further than a dynamic language.
There's no need for a single language to win over others: many can coexist so whatever style you like you can choose it. Most importantly, have fun programming and optimizing stuff :-)
Crystal fits a different niche. It's a nice, fast replacement for Ruby.
Nim is an incredibly powerful language, but with a fair amount of safety. I consider it the highest productivity language today, when maintenance costs are included. It sells itself.
As for JSON, I have the world's fastest JSON parser (based on vivkin/gason), but I cheated. (I have other plans for it.) There are many ways to cheat.
- Don't construct actual dictionaries.
- Delay decoding of Unicode or floats.
- Retain pointers into the input buffer.
- Avoid deserialization.
- Etc.
gason benchmark, Clang 3.4 (tags/RELEASE_34/final), x86_64 1, SIZEOF_POINTER 8, NDEBUG 0 Parse Speed (units/s) 814.14 71.13 rapidjson normal 784.22 73.84 rapidjson insitu 554.78 104.38 gason 181.47 319.12 nim-gason
Benchmarks can be gamed. I worry only about whether idiomatic code is reasonably efficient. Nim seems pretty good at everything.
Library benchmarks tell you little about the language; their performance is generally a function of the library implementation. LAPACK isn't fast because it's written in Fortran, but because a ton of work has gone into tuning the library, both mathematically and for modern CPU architectures.
Language performance these days is 90% about feeding your code in a suitable form to the established backends (LLVM/clang, gcc, JVM, CLR) and rely on the expertise of their implementors to do the actual heavy lifting [1]. As long as you can represent your data in a reasonably backend-friendly form, identical algorithms should result in roughly comparable performance for the same backend. Crystal has to do more work here because it has to infer types, but once it has (say) figured out that a variable is a plain integer, it can generate exactly the same code as a statically typed language does where the type is explicitly declared.
[1] The exception is if your team consists of actual backend gurus (e.g. V8, Dart, LuaJIT) who can do that themselves, but those are few and far between.
Libman: their policy of "never have to specify the type of a variable or method argument".
This just means that they do aggressive type inference, a la Haskell. Since Crystal is, nonetheless, a statically typed language, this has no bearing on performance.
It looks like Crystal has an LLVM backend. While I find the notion of a language producing intermediary (ugh) C, questionable, it seems a bit less hairy to have your language produce C, and require the existence of a "real" compiler, than have your language use the LLVM virtual machine, and require the existence specifically of the LLVM. Nim can (in theory) work seamlessly with LLVM, gcc, and a number of other C compilers, and someone even got cygwin to work, I think. As nice as it is that LLVM made a standard "compiler framework" backend thingy, what it amounts to for language design is effectively the same as just producing C, but also tying yourself to one single backend.
Also I hear tell that LLVM is pretty huge compared to some other compiler suite thingies. Rust (auuuuugh) for instance takes hours to compile on my machine, due to the resource requirements of preparing the LLVM stuff. Going through the massive effort to learn the binary LLVM plugin backend architecture thing, when you can just spit out a kilobyte of ugly C and end up with the same damn thing always makes me question the choice to use an LLVM backend.
So I'm skeptical about the utility of Crystal, though I can bet it beats the pants off of RubyCorporate2.0Edition or whatever they call the optimized ruby thing.
Although aggressive type inference really is awfully nice...
Approach based on intermediary C code has one more advantage over Rust way: portability. Porting Rust to DragonflyBSD seems to be very troublesome - "porting" Nim to NetBSD requires single make command. And that make me very happy.
PS. Yes, Nim works on NetBSD - at least with default gcc, but I'm also playing a little around PCC available as alternative. :)
Yup, compiling to C is definitely the way to go, at least in the beginning. As a {F,O,D}BSD advocate, I greatly appreciate the instant portability. When running Linux (ex. on a work laptop), I appreciate being able to compile Nim without compiling all of LLVM. Choice of compiler can also get you better performance on some platforms (ex. Intel's icc optimized for Android, Solaris CC, MS vcc, AIX xlc, etc), but I think LLVM is now close to being the champ on all platforms.
I'd love to see Nim do more aggressive type inference and come ever-closer to Python's brevity. Perhaps even assumed "import"s too - shaving off lines of code is a good thing. As I suggested earlier, appealing to Python fans with something almost as productive but a lot faster would be a winning strategy for Nim - just gotta inch forward on both those fronts: performance (esp for Web apps) and Pythonic code briefness...
to be fair, I feel that (in general) the critical approach to OOP that people tend to have nowadays is refreshing. c.f., Stroustrup's OOP without inheritance
IMO, what most people need from OOP is dot notation :D I really like Nim's uniform function call syntax, and, for now, I really haven't missed inheritance (but I confess I never developed something large with it); OTOH, applications I've been developing lately (not Nim) usually DON'T have very deep inheritance chains
vtref really is cool, powerful and unique; my only fear is that it might be hard to grasp for people looking for a cpp/java-like OOP. This should be addressed with clear documentation and examples
Crystal has gotten a highly distinguished mention in this month's update summary of the prestigious TIOBE prog lang popularity index:
The top programming languages are in a long term decline: both Java and C have all time low scores in the TIOBE index. And almost all of the other top 10 languages are going down as well year to year.
So what languages are taking advantage of this? It is all happening down in the charts around position 40. A new set of languages is gaining ground, notably Crystal (#32), Kotlin (#41), Clojure (#42), Hack (#43) and Julia (#46).
Especially Crystal with its jump from position 60 to 32 in one month is doing very well. The Crystal programming language is a statically typed Ruby variant. Since it is compiled it is superfast and has a small memory footprint without losing the feeling of being easy to use. It seems worthwhile to give it a try.
This is very impressive, given that Crystal is a 3-year-old newcomer that we've watched take its first steps, while Nim is still fighting its way into the top 100...
This has just made me realise how unreliable TIOBE is. This is one of the searches that they use: +"crystal programming". Last time I checked (when I was submitting Nim to TIOBE) the number of results was around 20k. Now it's 256k? Go's is 331k (and strangely enough it was 230k, less than 5 minutes ago when I checked it before writing this post, proving once again how unreliable this is). D's is 225k.
Now, sure, TIOBE uses multiple search engines to make its ranking. But I doubt it makes much of a difference to the reliability of these results. I could be wrong but I seriously doubt that Crystal is so close to Go's popularity and that it's more popular than D. And actually TIOBE itself lists Crystal (#32) as being more popular than Clojure (#42), I'm very sceptical that this is the case.
It seems to me that results such as these are giving this result count boost. They obviously have nothing to do with the programming language.
IMO we can nowadays do a much better job at finding the popularity of a language by combining a number of indicators. For example: commit frequency on GitHub, number of watchers on GitHub, number of projects in that language on GitHub, activity and number of users in the respective language's IRC/Gitter/Slack channels. I would have built something that combines all of these already if I had the time because I am very interested in such statistics.
So yeah, let's take this with a pinch of salt.
I didn't mention javascript backend because Typescript/Flowtype/Clojurescript/Scala.Js/Purescript/Bucklescript etc have far better tooling than Nim so I personally don't see Nim as attractive for doing javascript target stuff.
I wish to revise this point to mention a few positives about javascript and the Nim. The Nim compiler is extremely fast for javascript target compared to most other options. Also you can achieve a lot with Karax with relatively terse code and the build release can be also relatively minimal.
OOP is the big exception as far as language semantics are concerned.
Agreed, especially of Nim were to attract Python programmers used to object oriented Python. Looking at the examples on https they're a lot more complex (in my opinion) than an equivalent Python class, which I would argue increases the barrier to understanding and therefore barrier to adoption.
It'd be great if Nim had specific object oriented semantics, including the enforcement of the 'off-side rule' on properties and methods under a Nim object, to help make this a lot easier for those used to object oriented Python to adopt it.
I don't think the class-based syntax is the problem.
See this video by Brian Will where he explains the real problem with OOP: https://www.youtube.com/watch?v=QM1iUe6IofM&t=2158s&pp=ygUKYnJpYW4gd2lsbA%3D%3D
Or this very short video on Inheritance v. Composition (another reason people dislike OOP): https://www.youtube.com/watch?v=_hb_oRhASuw
I think, if you are coming from an OOP language, the syntax will take some time to get used to. I think it's only a trivial thing.
This is what happens when you try to use that 7 years old unmaintaned repo
interfaced.nim(40, 39) Warning: Deprecated since version 0.18.1; All functionality is defined on 'NimNode'.; symbol is deprecated [Deprecated]
interfaced.nim(40, 24) Warning: Deprecated since version 0.18.1; Use 'strVal' instead.; $ is deprecated [Deprecated]
interfaced.nim(41, 41) Warning: use `getImpl: NimNode -> NimNode` instead; getImpl is deprecated [Deprecated]
interfaced.nim(126, 28) Warning: Deprecated since version 0.18.1; All functionality is defined on 'NimNode'.; ident is deprecated [Deprecated]
test.nim(45, 21) template/generic instantiation of `toAnimal` from here
interfaced.nim(107, 17) Error: expression cannot be cast to 'RootRef'