The consensus in this thread is that Crystal has lost a lot of steam recently and little is being done. In fact, it's been nearly 6 months since the last release. As a member of the core team, I'll be honest: this is correct.
Manas, Crystal's main commercial backer has had a very tough year and has been unable to donate as much manpower to the project as previous years. The non-manas members of the core team have had difficulty finding time too. This has been a low point in the project, but it's very possible that 2018 will be a different story. Development is picking up right now, extremely basic windows support is just a few days from being merged into master, and the 0.24.1 release is imminent. There will be a blog post regarding this on crystal-lang.org soon.
I have had my eyes on Crystal for a while now, and one of my New Year's Resolutions for 2018 is to learn it.
I really hope the project does not collapse. What little I have seen was very, very exciting. I used to love Ruby, I did most my scripting in it for pretty long time.
More recently, I have discovered the joys of strong static typing. A language that combines Ruby's expressiveness with type safety and good performance (let's face it - ruby's performance is not that impressive) sounds really sweet.
This is definitely a language I would like to get to know. ;-)
Do you think it's reached the point where Crystal should consider some way to get funding outside of just Manas to allow for some full-time development on the project? I think it deserves full time developers, and there may be sources of untapped funding for potentially doing this if there is interest.
It may also make sense (at some point) to consider putting together a nonprofit that could make it easier to get access to this sort of funding in the future.
I'm not sure what you mean of "outside of manas" but we have a bountysource [1] which contributes to the hours of the Manas developers to work on Crystal.
Well if the sponsor company is having a "tough year" as you said, and that means they can no longer support full-time development of Crystal anymore, it makes sense to put together a structure so that your core team can work on it full time. In the future you're going to need more than an internet tip jar to get access to the better sources of funding you'll need, which are largely institutional.
It's going to be the most depressing news ever if we're doomed to a hundred years of toilet dumpster programming languages because Crystal loses steam due to one company's tough year. Let's make sure that dark future never happens.
Crystal seems like a great high-level language and alternative to Ruby, but the biggest blocker for me using it more generally is the lack of multi-threading support. There is WIP support for threading but it has not been committed. Moreover the Crystal developers either don't do a great job of updating their multi-threading roadmap [0] or work on multi-threading has died off. (I assume it is the latter because the thread-support branch has not gotten any commits in 10 months [1].) Ultimately if I wanted the high-level aspects of Ruby or Python and wanted more performance and great thread support I'd probably go with Elixir (even taking into consideration BEAM weirdness).
It'll be going away -- rewritten in Rust. Crystal has been great, and I really like the way it started going, but...
Development of the core language seems to have died off -- either through lack of updates and communication like you've mentioned or development. Regardless, the core team doesn't inspire me enough to take a chance on the language any more -- I've lost hope for a 1.0 release any time in the near future. I'll be killing off my BountySource[0] recurring donation as well.
There was/is so much potential in Crystal -- it really is great (joy to write, performant) -- but without real sponsorship/leadership, it just seems to be floundering. I hope I'm wrong and these are just growing pains!
It'd be great if you could stick around for just a few more months. As a core team member external to Manas, I know how frustrating developing Crystal has been recently but I'm hopeful it'll pick up again in the next few months. Check my top-level comment.
Thanks for the honest insight -- really, that's so much better than the general silence we've been seeing.
A regular "State of Crystal-lang" blog post would be sufficient for me to hang on. Even if the posts might be "haven't done much. this is what we'd like to work on next, these are the roadblocks, etc. you can help by doing XYZ".
For the kind of applications I work on (compilers and interpreters) we have large, mutable data structures. The best way we know as an industry to parallelise operations on these data structures is via shared memory, which either means threads, or processes with shared memory mapped into them, which is effectively the same thing (you need the same synchronisation).
For me personally, that's what's so good about multi-threading. It's the best tool for my job.
Any web application is going to be almost entirely network and database-bound, not CPU-bound. CPU-bound tasks are stuff like scientific computing, video encoding, the Ruby interpreter and Crystal compiler themselves, etc.
The advantage of multithreading in a web application is not "using all cores" but rather that the application can spawn a thread to "asynchronously" wait for a response from the network or database while it continues doing other work in the main execution thread, instead of "synchronously" blocking all work while it waits.
In other words, web applications leverage concurrency with their use of multithreading, not parallelism.
I will caveat that having more cores can give concurrent applications a small performance improvement, because even though each thread is only using a CPU core at around 0 to 15% (in short bursts) of capacity, switching between two threads on the same core is a context switch, a fairly expensive operation. More cores means fewer context switches.
But, unlike with parallelism, making use of more cores is not the reason concurrency offers performance benefits. ~99% of the performance benefit of concurrency is realized on even a single-core machine, whereas parallelism by definition requires multiple cores to offer a speedup. Concurrent applications such as web servers (e.g. Apache, Nginx) typically spawn hundreds or even thousands of threads per CPU core.
While this argument is valid and fine, I think it has mostly played out. Previous language design choices that consciously ignored multithreading were made in the mid-late 90s when the future of consumer SMP was unclear. Now it is clear that multi-core is and will be an important component in everything -- we have quad-core phones, dual-core cameras, etc.
While an application may be fine without good/native support for parallelism in most cases, it's really nice to be using a language that allows you to bolt it on easily when it turns out that you _do_ need it. At this point, it's a language feature that's not worth leaving on the table without compelling rationale.
Arguably, the class of high-level dynamic languages that were on the rise in the mid-00s (Python, Ruby) lost a lot of steam to newcomers like Swift and Go due to the language designers' hesitation around embracing parallelism. Crystal should not repeat that mistake.
This is exactly the reason I'm concerned about investing in Crystal (and OCaml, where multicore is also in an awkward state). I need to be confident that when I need parallelism, the language supports it (good threading primitives are imperative too, so not just pthreads).
We really really want to support parallelism and we've had some basic successes so far. We're very confident we want this, and we're confident we can come up with a performant solution: despite being a different language our parallelism solution will likely end up close to Go's which has largely solved this already.
We're not there yet but I'm confident the main barrier is time and manpower to implement it, not willpower or technical reasons.
I'm not saying that Crystal or any language shouldn't support multithreading or parallelism, but that web applications (that is, almost all existing commercial Ruby applications) don't employ and wouldn't be able to benefit from parallelism. This is due to the laws of physics: two CPU cores connected over a network cannot communicate faster than two cores on the same silicon die, so network communication is impossible to meaningfully parallelize at the CPU level. (But, those same characteristics make concurrency a massive performance win).
>the class of high-level dynamic languages that were on the rise in the mid-00s (Python, Ruby) lost a lot of steam to newcomers like Swift and Go due to the language designers' hesitation around embracing parallelism.
Quite the opposite, Python has a dominant and rapidly-growing market share (as the developer-facing API wrapping Fortran/C/C++ libraries) in CPU-bound, massively-parallel applications (NumPy, Tensorflow, etc).
Swift and Go are odd examples to make your point: Swift's support even for concurrency[1] is a low-level and unergonomic Objective-C library, and for parallelism (or implementing other concurrency primitives) an even lower-level and less ergonomic paper-thin Objective-C wrapper around pthreads.[2] That's still better than Swift on Linux, where the only option for either is plain pthreads.
Go has excellent concurrency support (with goroutines, aka fibers, aka green threads, which execute one-at-a-time on a single CPU core and from within a single actual OS thread), but for multi-core parallelism its standard library also offers nothing more than a paper-thin wrapper around pthreads.[3]
Even C++11 offers a substantially higher-level and more ergonomic parallel-threading API than either Swift or Go.[4] Among recent, relatively mainstream, general-purpose languages, the examples that come to my mind as exemplifying strong parallelism support are Rust, Julia and recent versions of Java (Streams) and C# (TPL, PLINQ).
That the API support is awkward is not so important as that the functionality is at least accessible. Someone can always soup up a lackluster API (or, more likely, find a third-party library that has already done so for them), but elements of the core language implementation like the GIL are not so easily hand-waved away, as anyone who has followed Python's GIL saga knows.
Like I said, your argument is totally valid, and I don't contest it. It's not that most applications really need native threads, or that people are choosing the best alternatives for threaded applications. It's just that people don't want to use a language that will obstruct access to OS-native threading should they decide they need it, partially because it's hard to accurately anticipate all of an application's needs ahead of time.
FWIW: Crystal `HTTP::Server` can already use `SO_REUSEPORT`, allowing multiple web servers to run on a multi core machine. This is obviously only useful for web servers and is different from parallel processing.
This is a common complaint (not just for Crystal), but I would argue that most people do not actually need or want shared memory concurrency. They need either parallelism and asynchronicity, for which threads are just one possible implementation.
I'm assuming here that this is not just about multiple threads of control (which can also be achieved through having multiple processes), but having threads interact through shared memory rather than through message passing.
The problem is that with shared memory, there's a constant tension between performance and safety. Pretty much all safe approaches introduce either contention (through mutexes or other synchronization constructs) or considerable overhead (such as the O(log n) factor typical for purely functional data structures or creating duplicates to avoid contention). In order to write actual high performance code accessing shared memory, you will typically have to deal with inherently unsafe constructs.
In most cases, it's far easier to optimize message passing, perhaps throwing in some specialized shared memory constructs (such as Mnesia for Erlang) in order to make some common use cases less cumbersome. Note that with current memory hierarchies, the overhead of message passing compared to shared memory may be less than you think; any data that's actually used by more than one core has to pass through the L3 cache at a minimum.
I think that the reason why more people don't use message passing approaches is (1) that few languages actually support it well and (2) that message passing as a programming paradigm can be far more intimidating than shared memory, because causality is more difficult to reason about (though the ease of use of shared memory models can be deceptive and it's easy to run into difficult to diagnose correctness or performance problems). But technologically, message-passing is much easier to support. I've written highly parallel code in Python, Ruby, and OCaml, all of which have a GIL and cannot support shared memory parallelism. But they all have (near) universal serialization, so implementing basic message passing is trivial.
As an example, one of the main attractions of Go is, I think, that it provides a very accessible programming model for message passing (building on top of CSP). And we have PGAS approaches, which essentially simulate shared memory on top of a distributed architecture.
This is not to say that message passing is a panacea; there are plenty of use cases where it's a poor choice. For example, I'm currently working on a problem that is essentially about performing a reduction operation in parallel on multiple threads, where the operation is typically defined through a multi-GB data structure. Copying that data structure to every single thread is prohibitive, and keeping it in one thread would kill all the possible parallelism. But message passing is plenty good enough for 90% or so of all use cases.
Generally, I would phrase it so that the problems that shared memory and message passing have are duals of each other: shared memory is concerned with contention as a source of cost and complexity, message passing is concerned with communication as a source of cost and complexity. For the majority of problems, you can engineer solutions that fit either model (though they may look substantively different), so not using shared memory is not going to be a problem for those.
Asynchronicity is a different concern and does not actually require threads.
Worth nothing: Thoughtbot is working on a web framework for Crystal, called Lucky: https://luckyframework.org/
It looks quite promising, but as others (including core team members) have noted, development on Crystal seems to be losing steam. It's tempting but seems like a bit of a gamble to pick Crystal for anything serious, especially with great new languages like Elixir and Go gaining traction.
Seems to have a lot of promise. I wrote off Crystal some time ago but Lucky has my interest piqued and I’ve been building a few things with Lucky recently.
Crystal is a Ruby inspired compiled language, allowing it to run blazingly fast with a very low memory footprint. It uses LLVM for emitting native code, thus making use of all the optimisations built into the toolchain.
That makes no sense to me. Most of what makes Crystal different from Ruby results from the former being a static language and the later being dynamic.
I understand that dynamic languages are not popular these days on HN, but it's silly to suggest they should become static languages. They just offer different tradeoffs, a bit like screws and nails.
Dylan is a Lisp (thus dynamic) with Algol-like syntax.
Dylan supports AOT compilation like Crystal, and had optional type checking, so one could make it into Crystal if all variable declarations happened to be annotated.
Don't forget Dylan was intended to be used as Newton systems programming language and the team managed to create their own OS, even after C++ was decided to take Dylan's role.
Personally dynamic languages without AOT or JIT support were never popular with me beyond shell scripting tasks.
I really want a nice OO language with Garbage Collector / ARC that compiles to native code on Windows, Mac and Linux. Swift is good, but Windows story is weak.
I am personally waiting for Kotlin Native to mature. Until that time Go it is.
C#, on Windows via .NET Native and NGEN.
on Mac and Linux via Mono AOT (or CoreRT which is WIP)
on iOS via Mono AOT
Java (which I think is nice) also has quite a few commercial JDK native compiler options, Excelsior JET license is free for open source projects and OpenJDK is improving its own AOT story.
Red [0] has a fantastic cross-compilation story, is a tiny compiler, and is a kind of Lispy version of Object-Oriented. Includes GUI support in the native runtime. It's meant to be a sort of "better Rebol".
Red (and Rebol) are fantastic but unfortunately the learning curve is very steep for people that don't know it. I spent a month reading everything on the internet about Rebol, then Red was obvious but before that it's really hard to get into that world. IMHO Red should have something like a definitive book covering all aspects in one place (like [0]) because it has a vast array powerful features (URI, money, time literals, parse and other dialects) etc. Without concise but a complete guide the language appears to be just a weird "let's use [ ] instead of { }" construct.
Because "already know it" is not a single defined point on that curve but rather a region. It's easy to prove by negative, if "already know X" would be so easy to pinpoint the entire recruiting industry would cease to exist.
I've tried out Red a bit recently and REBOL more earlier, both are good. But IIRC, the GUI support (for Red) is not there equally on all the 3 major OS platforms (Win/Lin/MacOS) yet, last I checked. Hope it gets there, though.
Delphi. Similar to (inspired) C#, a non-C-style language, has ARC on mobile and Linux, and you can use the same idioms on Mac and Windows until the feature arrives for all platforms. Compiles to native. Nice features like RTTI, great UI, database, similar libraries.
Disclaimer: I work for the company that makes it :) FWIW, I joined because I like it so much.
Having inherited maintenance of an in-house application written in Delphi, one thing I really come to love about Delphi is that there have been no nasty surprises from the syntax side. This might not sound like much of a compliment, but people who have had to decipher overly clever C or Perl code[1] will probably agree this a huge bonus.
[0] I know, there is Free Pascal and Lazarus. I took both for a spin, hoping it might help me get to know Delphi, but Lazarus has crashed on me so frequently I quickly gave up.
[1] Or C++, I guess. The only C++ code I have had to look at was really more like C + MFC and was devoid of any excessive cleverness and a marble of clarity. I imagine there are many crawling horrors out there written in C++, but I have never seen one myself.
> I really want a nice OO language with Garbage Collector / ARC that compiles to native code on Windows, Mac and Linux.
SBCL[0] compiles to native code on Windows, Mac & Linux and is garbage collected. It implements an extremely nice OO language, Common Lisp[1]. The language is stable, and it's extremely powerful. SBCL is extremely fast as well, as fast or faster than C in many cases, when appropriate type declarations. Because it's by default a dynamic language, though, you don't need to declare types where you don't want to bother.
.net (C#/F#) has always had AOT compilation and is mature and now cross-platform. If Go is OO enough for you (it's not really OO in the traditional sense, so I'm not sure what you mean by "OO" if Go counts) then OCaml, Rust or even Haskell might also be good options.
No, Go isn't OO enough. That's the part I am looking for. Otherwise, Go clears rest of the needs. It compiles to native code which works on Windows, Mac, and Linux. Also, it is a stable language, with good tooling and huge library ecosystem and a strong backing by a well-known company.
Not OP here -- but I've been putting off re-writing a mess of a nodejs to binary (nexe = huge binary, gah!) implementation of a simple utility http service in hopes that Crystal would gain windows support. I've been stalling for a few months.
I have seen that you were able to make some progress here, awesome!
It looks like Windows support for Crystal is too far off on the horizon, however. I'll be starting the re-write in Rust instead, unfortunately.
I think there could be strong demand for Crystal on Windows. There are a lot of ruby devs out there or people who simply prefer writing ruby-like code, who would automatically jump to crystal _just_ for this reason.
Mainly because I am not familiar with Ruby. But if Krystal emerges with a strong library ecosystem, I will definitely consider it.
Another factor is backing by an established company. Unfortunately Nim (I like Python a lot), D and Crystal are languages supported by relatively small communities. But I understand all languages start somewhere. Python, PHP, and Ruby are examples where one person languages eventually became very popular.
I won't mind doing small CLI projects in Crystal, but for something that I do for a client which needs to stay in production for years, strong backing by a well-known company goes a long way.
In fact, Go is an easy sell these days in the enterprise, once you drop words like Google, Docker, and Kubernates. Unfortunately, it's not sufficient to understand merits of a language to get your company/client to use a language. Business decision makers need some kind of social proof.
Ruby like syntax huh. Man I wish the Python syntax would catch on more. Most clean and readable code I've ever laid my eyes on. Wish I could use it but it's just too slow for the stuff I do. What would I give for a language at least the speed Java but with a Python like syntax.
I guess you are a Java enthusiast (in my experience Java coders just tilt with Ruby). Anyway you should try Elixir, it is Ruby "The right way" in my opinion.
Please let's stop spreading this "elixir is like fast/better ruby". The syntax is not even that similar, and the runtime is completely different, the patterns and tools available not the same at all.
I know it's probably a hill on which I'm gonna die and get a lot of downvotes, but we should really try to not force Elixir into being a new, fast ruby and let it evolve and solve problems in its own ways.
"elixir is like fast/better ruby" is simplistic but it's also not entirely untrue. When I first started playing with Phoenix I found it palpably similar to Rails, but also faster and better in a lot of ways, especially the WebSockets story.
When I started writing Elixir I didn't know how great it was nor did I understand how different it was from Ruby. The BEAM is a challenging thing to grok. As I used it more and more, I did learn and appreciate those things. But at no point, did I feel deceived about how similar Elixir and Ruby were supposed to have been. I was surprised and relieved by how many of the things I had to worry about in Ruby, I didn't have to worry about in Elixir.
At the end of the day, Elixir's similarity to Ruby is part of what gets a lot of people like me in the door. It's a hook and I think it's a good one. I mean, sure, we're going to have to explain to people why they don't need something like Sidekiq, and we're going to do it many, many times. But that's what we do as a welcoming community.
I guess it's a balance. If we have a lot of people trying out Elixir and leaving because they feel they were misinformed, that's definitely relevant. But I don't hear much of that. On the other hand, if we can use this similarity to get OO people to try out this crazy, awesome FP language that uses the actor model, that could really help grow the language.
You're on point that Elixir really is a completely different paradigm, but I don't know if I agree that it really matters from a marketing perspective, particularly when marketing to Rubyists.
> when I first started playing with Phoenix I found it palpably similar to Rails, but also faster and better in a lot of ways, especially the WebSockets story.
Thing is virtually every language that is used for webdev has a framework that is similar to rails. So that's not much of a discriminatory when comparing languages...
The number of languages that do WebSockets well include what would be a small subset of "virtually every language that is used for webdev [sic] has a framework that is similar to rails".
But that wasn't really the point of the comment. The point, for those who didn't feel the need to read all the way through, is that Elixir's surface level similarity to Ruby is enough to make Rubyists, or at least this Rubyist, feel reasonably comfortable, reasonably quickly. Making the Elixir/Ruby comparisons may have some annoying side effects, but they're probably good for the growth of the language.
> Please let's stop spreading this "elixir is like fast/better ruby"
It's just my opinion, and I didn't make it up, I'm a Ruby on Rails developer with 7+ years of experience and I played enough with Elixir/Phoenix in order to come to a sensible opinion (I even contributed to Phoenix)
Its true, I'm more used to C like syntax, but I have no issues with Lisp, Python or MLs. I think Ruby reminds me of Basic, and I similarly just can't enjoy myself when using Basic syntax.
I understand a lot of people enjoy it, not trying to blame it, its me I'm sure, not Ruby. That said, I've seen a few languages like Elixir and Crystal come up and they use "Ruby like syntax" as a selling point. So I'm trying to understand, what does everyone who likes the syntax find so great about it?
> In Crystal, as in Ruby, everything is an object. [...] When you write Crystal code, most of the time, you write classes.
Why, God, why?! Really, OOP is a great way of structuring lots of types of code (not all!). But I don't think it really helps anyone to treat everything as an object. Some things are not objects they just are whatever they are. A function is just that, a function. If I have to call a `.call` method on a "function" as in ruby, then that's simply not a function anymore. And... it's not an object either semantically, because objects are about things that can receive and send multiple messages and sometimes encapsulate state... and that's not how anyone thinks about functions!
By making everything be an object, you actually f up the concept of "object" itself...
I like Ruby's elegance, but every time I see the "OOP at the core" part, I suddenly get more love for Python and Javascript for at least getting this right.
I have never used Crystal before, but I don't understand how you can be against "everything is an object" and then say Python and Javascript are different... Pretty much everything in Python and JS is an object... So really, what does fundamentally differ?
Sure, one can argue that nothing ever fundamentally differs because all languages are Turing complete, but...
When I decide to use functions in a language that has objects, 99% of the time it is implied that a function either
1. holds no state, aka "referential transparency"
2. if it manages external state, it does it by modifying one of its arguments (usually the first one and in a very obvious way)
This allows nice mixing of functional and OOP!
("mixing" by keeping things separate, paradoxically :P...)
I know that if I see a piece of code with a dance of functions that take in and/or generate functions etc., that's a "functional zone" of a program and those function obviously don't have state.
When the only way of passing functions to functions and returning them is basically the same thing as passing and returning objects, you'll always be lost when reading someone else code and have to ask yourself at every step: "is that thing that receive the `.call` message maybe also holding some state". Or you'll always be unable to just assume that a function is referentially transparent, because "it's OOP, object aren't RT, dooh".
This places a huge mental burden when reading/understanding other people's code!
(And yes, in Python or JS you can also treat functions like objects and even attach fields and methods to them, BUT there is no not-extremely-awkward or obvious way to mutate those from code inside the function. Referring to the function by name makes it obvious. Using stuff like `arguments.caller` is awkward and widely accepted as bad practice. Only static variables in C++ allow such "muddying the semantic waters" practice easily, but they are also bad practice and easy to grep for.)
Python kind of cheats, though. Not everything is an object -- most low level things like arrays and numbers aren't. Ruby is from the Smalltalk tradition where everything literally is an object -- and I say that as a good thing. Even the "functions" that you can define are really methods of the Object class -- it just happens to be the case that Object.foo(x) can be abbreviated as foo(x).
>And... it's not an object either semantically, because objects are about things that can receive and send multiple messages and sometimes encapsulate state... and that's not how anyone thinks about functions!
Well, functions can "sometimes encapsulate state" (closures), and can send and receive multiple messages (.call, .toString, etc depending on the language).
>I like Ruby's elegance, but every time I see the "OOP at the core" part, I suddenly get more love for Python and Javascript for at least getting this right.
Almost everything is an object in Javascript as well (just in a botched way). The difference is in how inheritance happens, not in having objects or not. Ditto for Python.
Functions are objects in Python and JS, btw. And primitives can be treated as objects. They are wrapped by prototypes in JS, and belong to type classes in Python.
Ruby's elegance stems from being able to treat everything as an object sending messages to other objects, like Smalltalk. Some languages promote a certain style of programming. You might as well complain that Haskell is functional, and Lisp is homoiconic.
Howevver, Ruby & Crystal still let you define functions and write statements as if they were procedural.
my issue is not with writing procedural code, it's with the way functional parts of a program "mush" into "OOP-statey" with no obviousness of boundary. It feels kinda "dirty".
This actually isn't really the case in Crystal, or at least not as much as in Ruby. We allow you to create a proc (closure) from an existing method/function but methods themselves are just methods. Classes have metaclasses though, which are fairly useful for some neat reflection-like behaviour.
None of this really matters, even if functions were objects, then as long as that fact gets out of your way just don't take advantage of that.
> I like Ruby's elegance, but every time I see the "OOP at the core" part, I suddenly get more love for Python
Python does the same thing; functions are objects. Ruby needs a call to .call or the equivalent .() syntax because of optional parens, not because of pervasive OOP.,
Manas, Crystal's main commercial backer has had a very tough year and has been unable to donate as much manpower to the project as previous years. The non-manas members of the core team have had difficulty finding time too. This has been a low point in the project, but it's very possible that 2018 will be a different story. Development is picking up right now, extremely basic windows support is just a few days from being merged into master, and the 0.24.1 release is imminent. There will be a blog post regarding this on crystal-lang.org soon.