Overview of the Crystal language
marmelab.com
marmelab.com
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.
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.
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.
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. ;-)
[0] https://github.com/crystal-lang/crystal/wiki/Threads-support
[1] https://github.com/crystal-lang/crystal/tree/thread-support
And if speed is important to you, then you want to use all 4/8/16 cores of your machine, not just one.
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 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.
We're not there yet but I'm confident the main barrier is time and manpower to implement it, not willpower or technical reasons.
>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).
[1] https://developer.apple.com/documentation/dispatch
[2] https://developer.apple.com/documentation/foundation/thread
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.
For me personally, that's what's so good about multi-threading. It's the best tool for my job.
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!
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".
Looks like we can probably do at least one blog post a month, we'll discuss it internally.
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.
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.
Website: https://crystal-lang.org/ Github: https://github.com/crystal-lang/crystal
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 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.
But internal politics and battling between teams spoiled it.
Check the comments from mikelevins and wrs.
I am personally waiting for Kotlin Native to mature. Until that time Go it is.
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.
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.
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.
Edited to add: (for Red)
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.
The language clarity is part of the language's beauty, yes, definitely agree. It's very nice to work in.
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.
[0] http://sbcl.org/
[1] http://www.gigamonkeys.com/book/ http://www.gigamonkeys.com/book/object-reorientation-generic... http://www.gigamonkeys.com/book/object-reorientation-classes...
* Python-like syntax
* Compiles to C/C++/JS/Object-C
* Supports Windows, Linux and Mac.
* Multithreading
* Extensive metaprogramming
* VERY fast compiler
* Dead code elimination
* Very strongly typed, with related features (eg distinct types)
* Type inference
* Excellent structural generics, type classes, and compile-time concepts
Basically looks like python, runs like C. Because it compiles to C, you can run it anywhere you have a C compiler (which is almost anywhere).
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.
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.
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...
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.
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)
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?
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.
Likewise Prototype-based programming is OOP.
> import builtins
> builtins.int
=> <class 'future.types.newint.newint'>
class myint(int):
passExcept primitives that are not objects but (some) can be autoboxed to behave like objects.
A riddle:
var a = 2;
a.b = 3;
console.log(a.b);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.)
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.
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.
Afaik, both Ruby and Crystal allows you to write dumb functions and procedures, if you love C style structured programming.
def plus_one(x)
x + 1
end
So perhaps you should calm down a bit.Perhaps you should.
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".
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.,