So ruby fans want Crystal to get more publicity and python fans want Nim to get more.
Has anyone used both in any serious project, and if so can you compare? I've spent a tiny amount of time with Nim but found documentation, training videos, books etc extremely thin on the ground. Lots of Nim projects on github are ancient and haven't been updated in years. Chat GPT never gave me working code. How is Crystal in this regard? How would you compare their std libraries and available modules?
I read that during the first run, it will be slower because of the crystal code being compiled. Is the compiled code stored somewhere?
I am asking this because in that case, can’t I run it myself and share the code with the precompiled parts so others don’t have to experience the slow start?
Yes that appears to be the case. If you run the single file example in the README it will add a directory called "crystalruby" with generated source code in it.
So it looks like you could easily check that in with your gem.
I don't actually see any object code in the directory but this is a new project so they may not cache that yet but I don't see why they couldn't in future. e.g. as a build step when installing a gem.
This seems to be a fantastic bit of work so far with a lot of potential for the future.
Conceptually, isn’t that what a JIT compiler does?
Crystalruby ia really interesting though. This has got me wondering:
1. How this compares to Sorbet Compiler in terms of performance?
2. If you could use Sorbet or RBS type annotations to drive crystalruby?
3. How would YJIT perform if it was fed with type annotations?
4. Could crystalruby type signatures be used by Sorbet-lsp, ruby-lsp, etc in to improve developer experience?
I really wish we had a single standard for inline types in ruby. I wish that was an inline RBS type annotation but I guess I’d take anything that works.
It would definitely be possible, but I would really hate to see coupling like that added to ruby. I also worry that it would make it too magical and difficult to figure out what code is running, where and when. Debugging an issue deep in the bowels of that is a scary thought.
You won't ever see fast compilation times with it, and here's why:
> type annotations are rarely necessary, thanks to powerful type inference. This keeps the code clean and feels like a dynamic language.[1]
I prefer Sorbet[2] for Ruby, which is pretty fast and a reasonable compromise. People complain about the verbosity, but you can't have both speed and type inference. Pick one.
Some of the verbosity in Sorbet, however, comes as an indirect result of an underdeveloped Generic type system (the rest of the parts work great though). Some of the verbosity comes from lack of fluency with the tool, which gets better with practice, and doing research. The bottom line is I often think its a skill issue when people say it leads to code that is too verbose.
Kotlin strikes a good middle ground on the fast compilation <-> inference continuum IMO. within function bodies you rarely need to think about the typing system. Function return types can be inferred for expression bodies and star projections can make some of the more tricky parts of generics more tractable.
Inference is a big part of what increases Kotlin ergo over Java and while it does compile slower you are going from a very fast base so it doesn't feel slow relative to other options.
what annoys me to no end with sorbet is that it subtly pushes to worse looking code, even if to ignore all the verbosity and annotations.
let's say there's a generic data transformation in a method which could be extracted to a private helper method. Sorbet didn't have any issue figuring types of the result while those lines were in the parent method. But when those three lines are extracted, sorbet now requires you to write a generic signature for that method, otherwise type checking is not working anymore. Generic signatures are even more verbose and hard to write than for methods with specific types.
So, a kneejerk reaction would be just not extract those, because it becomes too much work, and readability is not improved anymore, because signature for three lines of code is taking five lines. And that's until someone "clever" will add a rule to rubocop limiting all methods to a specific amount of lines indiscriminately, so I'd spend additional work time imagining that person being punched in a face.
In theory something like that could be solved by marking helper methods as inlined, basically promising that you're not going to use them in subclasses or whatever and allowing the same inference to work inside. But "sorbet is feature complete".
Tried crystal several times over the years. Each time, I encountered Crystal bugs, surprises, oddities, and missing pieces such that there was no viable path forward to adopt it for anything serious. Elixir + Rust with rustler is pretty compelling as a scalable, viable alternative.
I hacked this together last weekend to scratch an itch and have some fun. This got a lot more attention than I was expecting so early on.
I've had to scramble a bit to start hammering this into something that's less prototype, more legitimate project. I hope it's now close to a cohesive enough state that someone trying it out will have a relatively smooth experience.
Given the interest, I definitely plan to sink a bit more dedicated time into this project,
to tick off a few key missing features and add some polish.
It would be great to see it grow into something that can be useful to more than just me.
Seems like there's definitely some shared desire for this type of tool.
It probably goes without saying that you probably shouldn't convert large amounts of mission critical code to depend on this (for now).
It's still early days and the API hasn't been "crystallized" yet...
Yes the idea isn't terribly new, but this implementation is significantly more elegant (and ruby-ish) thanks to what looks to be a great design and the capabilities of Crystal. rubyinline is neat but honestly not something I'd seriously consider using in a production project. I need to try out crystalruby before I have an opinion on that, but it looks very promising!
Has anyone used both in any serious project, and if so can you compare? I've spent a tiny amount of time with Nim but found documentation, training videos, books etc extremely thin on the ground. Lots of Nim projects on github are ancient and haven't been updated in years. Chat GPT never gave me working code. How is Crystal in this regard? How would you compare their std libraries and available modules?
Deleted Comment
I am asking this because in that case, can’t I run it myself and share the code with the precompiled parts so others don’t have to experience the slow start?
So it looks like you could easily check that in with your gem.
I don't actually see any object code in the directory but this is a new project so they may not cache that yet but I don't see why they couldn't in future. e.g. as a build step when installing a gem.
This seems to be a fantastic bit of work so far with a lot of potential for the future.
Deleted Comment
Crystalruby ia really interesting though. This has got me wondering:
1. How this compares to Sorbet Compiler in terms of performance?
2. If you could use Sorbet or RBS type annotations to drive crystalruby?
3. How would YJIT perform if it was fed with type annotations?
4. Could crystalruby type signatures be used by Sorbet-lsp, ruby-lsp, etc in to improve developer experience?
I really wish we had a single standard for inline types in ruby. I wish that was an inline RBS type annotation but I guess I’d take anything that works.
Coming from both python and statically-typed languages, your description 100% squares up with every experience I've had with ruby
Folks are free to throw stones at the Spring Framework similarly, it's not out of bounds, but ruby seems to embrace the monkeypatch
> type annotations are rarely necessary, thanks to powerful type inference. This keeps the code clean and feels like a dynamic language.[1]
I prefer Sorbet[2] for Ruby, which is pretty fast and a reasonable compromise. People complain about the verbosity, but you can't have both speed and type inference. Pick one.
Some of the verbosity in Sorbet, however, comes as an indirect result of an underdeveloped Generic type system (the rest of the parts work great though). Some of the verbosity comes from lack of fluency with the tool, which gets better with practice, and doing research. The bottom line is I often think its a skill issue when people say it leads to code that is too verbose.
1. https://crystal-lang.org/#type_system 2. https://sorbet.org/
> > type annotations are rarely necessary, thanks to powerful type inference. This keeps the code clean and feels like a dynamic language.[1]
There are fast type inference implementations. For example, the OCaml compiler is pretty fast
No reason Crystal couldn't improve this part of compile time in future
Inference is a big part of what increases Kotlin ergo over Java and while it does compile slower you are going from a very fast base so it doesn't feel slow relative to other options.
With OCaml you can pick two :)
let's say there's a generic data transformation in a method which could be extracted to a private helper method. Sorbet didn't have any issue figuring types of the result while those lines were in the parent method. But when those three lines are extracted, sorbet now requires you to write a generic signature for that method, otherwise type checking is not working anymore. Generic signatures are even more verbose and hard to write than for methods with specific types.
So, a kneejerk reaction would be just not extract those, because it becomes too much work, and readability is not improved anymore, because signature for three lines of code is taking five lines. And that's until someone "clever" will add a rule to rubocop limiting all methods to a specific amount of lines indiscriminately, so I'd spend additional work time imagining that person being punched in a face.
In theory something like that could be solved by marking helper methods as inlined, basically promising that you're not going to use them in subclasses or whatever and allowing the same inference to work inside. But "sorbet is feature complete".
Having to wait longer to see the results of one's work results in loss of focus and productivity.
Author of the gem here. Appreciate the attention!
I hacked this together last weekend to scratch an itch and have some fun. This got a lot more attention than I was expecting so early on.
I've had to scramble a bit to start hammering this into something that's less prototype, more legitimate project. I hope it's now close to a cohesive enough state that someone trying it out will have a relatively smooth experience.
Given the interest, I definitely plan to sink a bit more dedicated time into this project, to tick off a few key missing features and add some polish. It would be great to see it grow into something that can be useful to more than just me. Seems like there's definitely some shared desire for this type of tool.
It probably goes without saying that you probably shouldn't convert large amounts of mission critical code to depend on this (for now).
It's still early days and the API hasn't been "crystallized" yet...
But this does seem to be the first attempt at interop between crystal and Ruby, which is notable because crystal is effectively a typesafe Ruby.
https://github.com/Anyolite/anyolite
This seems way more verbose than the project in the post.