
Why Crystal Is My Next Language - sdogruyol
https://fgimian.github.io/blog/2018/06/06/why-crystal-is-my-next-language/
======
kodablah
It's like a language was picked first and then the blog written backwards to
come to the conclusion. So there's some languages in bullets and subjective
pieces that are reasons not to use them. I can do that with Crystal too...it
doesn't even support one of the most popular OS's natively. I mean, the author
says C++ is a better language than Go, that Google is the ultimate reason for
success, and that Go takes us back to C. That doesn't even make any sense.
"doesn't seem to click to me...generally didn't feel...feel the language is
rough" etc. How about just use the best language for the problem, instead of
choosing the language before the project. As I've mentioned on many of these
language-comparison posts (more like tweets) these days, just ignore them and
remain wise to your problem space. They are very juvenile.

~~~
timClicks
In my view, programming language choice is cultural yet is always framed as
something objective. It is even possible to "use the best language for the
problem"?

Even if it were possible to evaluate all current alternatives, we lack
knowledge of the future and therefore can't reliably include important factors
such as ability to hire into the team.

~~~
Accipitriform
"In my view, programming language choice is cultural yet is always framed as
something objective."

I think that's an objectively false statement.

There are many objective metrics by which a programming language may be
evaluated. Conciseness, clarity, suitability for realtime applications, and
efficiency come to mind.

"It is even possible to "use the best language for the problem"?"

It's certainly possible to use a suitable language for the problem. Some
problems are more forgiving than others, of course...

I will say that the idea of a "general purpose" programming language, suitable
for most tasks, is a good one. The current multitude of languages, while fun
and good for exploring new ideas, is taking a lot of brainpower away from
actually producing working systems. Also, I'll claim that there's no reason
that most programs shouldn't be written in an efficient language, and in fact
there may be a push for that in the name of datacenter efficiency.

It's sad that the most written programming language today is Javascript.

~~~
sam0x17
It's a slippery slope though. Conciseness, clarity, many of those are highly
subjective..

------
sdogruyol
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.

Website: [https://crystal-lang.org/](https://crystal-lang.org/)

Github: [https://github.com/crystal-lang/crystal](https://github.com/crystal-
lang/crystal)

------
fgimian
Hey guys, thanks for reading my blog post and posting it on Hacker News. :)

It was just an informal brain dump for me and nothing too serious, so please
don't take anything to heart. And indeed, I have only spent a limited amount
of time with the other languages on my list. I still intend to keep a close
eye on them all as time goes on.

I'll try to respond to various comments :)

Update: Hacker News is rate limiting me, sorry folks. I'll try again to post
some replies in a little while.

~~~
rbosinger
Thanks for posting! The crowd can get harsh and maybe not everyone realizes
that making it onto the HN front page briefly != "This is proven research and
you should all listen to me right now". Blogging can be about learning and
brain dumping and, IMO, you should carry on doing so.

~~~
fgimian
Thanks a lot for the encouragement, it means a lot :)

It is tough to read criticism but it's always a good opportunity to learn. The
comment about mypy for example was correct and I'm pretty sure I'll be trying
it out on my next Python project. I've updated the blog post with minor
amendments and corrections based on the feedback here.

Indeed this was my first time on HN (which took me by surprise) :)

------
banachtarski
The results he got for C and C++ make no sense and I question what
optimization flag was actually used if any. From the looks of it, he uses "-O"
which is equivalent to "-O1" which is really not the flag you want (this
optimizes for both time and code size).

*edit: with O2 I get a dramatic improvement and the C and C++ versions compile to essentially the same disassembly. Using O1 is definitely not going to produce an apples to apples comparison.

~~~
fgimian
Thanks so much for your comment. I had actually tried -O3 during my tests and
didn't notice a huge difference in this particular example.

fots ~ $ gcc -O -o fib fib.c

fots ~ $ time ./fib

433494437

real 0m0.749s

user 0m0.743s

sys 0m0.003s

fots ~ $ gcc -O3 -o fib fib.c

fots ~ $ time ./fib

433494437

real 0m0.747s

user 0m0.742s

sys 0m0.003s

fots ~ $ gcc -O2 -o fib fib.c

fots ~ $ time ./fib

433494437

real 0m0.741s

user 0m0.737s

sys 0m0.003s

Clearly this is just a little mathematical algorithm and more real-world
problems would likely produce a more dramatic difference.

What sort of differences did you see on your end?

~~~
lallysingh
How much optimization do you expect a loop with a simple addition to have? It
doesn't seem a representative workload for an optimizer.

~~~
rdc12
Except it is not a loop[1], but a non-tail recursive function (with dual
calls), if the compiler can optimise that to a loop that is actually
impressive.

Most likely function call overhead (and in the slower cases, interpreter
overhead) is probably what is being measured in the best case.

[1] In that it won't be a jump and check style loop.

~~~
mbel
> Most likely function call overhead

Quite likely: [https://godbolt.org/g/xGkCyA](https://godbolt.org/g/xGkCyA)
although it really depends on the compiler (gcc is smarter here). He also
seems to be benchmarking printing more than anything else (hence the
difference between C and C++).

Without versions of the compilers and explained methods of measurement, the
results posted on the blog are more anecdotal knowledge than any kind of
benchmark.

------
nepeckman
I feel like the author dismissed Nim pretty unfairly. Github stars and issues
are one signal for a project status, but its hardly conclusive. Nim's
development has been pretty consistent and is approaching 1.0. Ultimately, a
lot of language choice is subjective, but I think anyone looking for a
language in the fast-but-expressive category should at least try Nim.

~~~
freedomben
Definitely. He dismissed it mostly because he doesn't like camelCase? seems
like a silly reason to dismiss a language. I've been around enough different
ecosystems to know that style is something you get used to. snake case, camel
case, same-line curly brace, give it a few weeks and you won't really notice.
It's a silly reason to dismiss a language.

~~~
fgimian
True, I REALLY hate camelCase though ... hahaha But seriously, I do need to
spend more time on Nim and give it a fair chance on a project :)

Ultimately, I had to choose something to commit to for a while as there are
just not enough hours for me to learn multiple new languages outside of work
and with my various other hobbies.

But rest assured that Nim was absolutely a front-runner and I'm happy to
update the post to reflect this.

~~~
beagle3
> True, I REALLY hate camelCase though ...

I do too, and that's exactly why you should use Nim. It is, perhaps, the only
language in which the variables "camelCase", "camelcase" and "camel_case" are
the same (though CamelCase isn't) -- so you don't have to follow braindead
Java principles if you don't like them.

~~~
skocznymroczny
I'd consider it even worse. I like consistency in my codebases. Good luck
grepping for variable, when half of the folks will use variable_name, the
other half will use variableName and the last half will do variablename.

~~~
beagle3
Nim includes "nimgrep", so I have good luck grepping for a variable, and it is
supported in most common editors with a nim package. Nim also includes
"nimsuggest", which provides intellisense - also available in most common
editors.

And if you really insist, it is quite easy to write a simple program that will
unify the styles for you. In practice, it's not a problem.

In this respect, Nim inherits pascal's case insensitivity, and just adds a few
minor and arguably useful details (underscores are ignored, case of first
letter preserved). It was never a problem for Pascal (or BASIC, or Excel, or
NTFS or HFS+) and it's not a problem in Nim.

And being an early Python advocate and evangelist, I feel history is rhyming
again .. ."What? You use _significant spaces_ ? That's so stupid, and bound to
make everything fail. Yes, some people still dislike significant indentation,
but by and large, experience shows that it's not inferior[0] to token block
delimiters, and in some ways it is superior.

[0] as long as tabs are a syntax error ....

------
vinceguidry
I'm often curious as to what people are doing that makes them really think
they need to venture outside their chosen scripting language. I use Ruby
thinking I'll 'drop to Rust/Crystal' whenever I need to do something
"serious", but that need never seems to manifest. Whatever I code in Ruby
seems to just do the job and I can solve perf issues with asynchronous
constructs like a jobs server, or something like caching.

At this point I'm really starting to wonder if Ruby really is the One True
Language and everyone's going to be using it in 100 years. Like how PG thought
Arc would be the hundred-year language.

Does static typing really offer that much more over dynamic? I'm not at all
convinced that it does, when you get right down to it. Once Ruby gets a built-
in typing system, we'll get to answer that question.

I do believe that Ruby will eventually solve its perf problem and once it
does, its pleasing syntax and semantics will just draw everyone back to it the
same way it did the first time.

~~~
atombender
Former Ruby developer here. I believe we will look back at this period where
we eschewed static typing as a historical mistake.

Back in the 2000s I was using mostly statically typed languages (Java, C/C++,
ObjectPascal), and compared to those, Ruby and Rails seemed like incredible
productivity boosters. But we quickly found that the lack of typing led to all
sorts of issues. For one, you need _really_ comprehensive tests to determine
whether a change breaks anything. Refactoring becomes not just a chore, but
genuinely psychologically stressful as you begin to worry about things
breaking in production. And they frequently do. Duck typing is elegant, but it
only works so far as you know the range of types to expect. I very quickly got
tired of checking the validity of arguments in code and doing coercion such as
Array(maybe_array_or_maybe_not). And the nils, ugh. I never created so many
bugs as when I was writing Ruby code (though JavaScript is a close second).

A language with a rich type system (Haskell, Swift and Rust come to mind) can
eliminate entire classes of bugs. Not just by letting you validate the types
of arguments and return values, but by letting you describe your program logic
in terms of types. It's often said that Haskell programs rarely have bugs once
they compile, and while there are no guarantees, it's often true.

Give a statically typed language a try for a while, and you will probably
change your mind. I don't know about Crystal, but Go is a decent entry-level
language. (Many former Ruby devs seem to enjoy Go quite a bit.)

~~~
kazinator
I recently refactored my fork of the linenoise library (written in C) from
_char_ to _wchar_t_. C being statically typed made it easy to find all the
places that need to be updated if some declaration is changed from the one
type to the other. The editor gathers the compiler errors and takes you to all
of them. The result was broken though. For instance, in some places I replaced
_strcspn_ (complemented span) with _wcsspn_ instead of _wcscspn_. It has
exactly the same signature. This was caught by testing all the edit
operations, and a very careful diff review.

Type checking only works when you make a mistake which breaks a type
signature. You can call entirely the wrong function with the right type
signature. You can call _sin_ instead of _cos_ , or forget to take a _log_ of
something. You an swap the arguments to a subtraction: both are floats. Think
of all the numeric code in which lots of variables, arguments, array elements
and so on are all of the same type. Or string code in which there are numerous
strings. Or OOP code where you have two or more objects in the same scope of
exactly the same class, which can get mixed up.

The idea that you do not need a comprehensive test suite because you have
static types is horribly wrong.

~~~
atombender
C is not a great example here, because it's somewhat weakly typed. But it's
true that typing doesn't fix the identical-arguments problem.

------
bhuga
Crystal looks great in many ways, but it's hard to go all-in with a new
language that lacks parallel processing. Go, Elixir, and Rust all have great
concurrency stories, and their adoption has a lot to do with that.

It is a gorgeous language, though, with a very easy to read syntax. I'd love
using it if it had something like goroutines, which would look great with its
block syntax. Good luck to the team working on it; I hope it's a contender.

~~~
hamandcheese
Do you need truly parallel processing?

Crystal already has a great concurrency model, it’s just not parallel _yet_.

~~~
fgimian
Yep, this is true. I'm hopeful that this will come soon :)

------
freedomben
It's just me, but I view the lack of OO features in Elixir as a really good
thing. You still have structs and some other OOish type stuff, but the
emphasis on composition and modularity has really made Elixir programs a joy
to read and understand.

The lack of a single binary isn't a fully fair criticism. While technically it
is true, it doesn't describe the shipping experience. You will need Erlang
installed on the target machine, but using escript you basically ship a
"single binary/file" and it includes everything it needs to run. It's quite
elegant, and erlang is super easy to install on basically all *nix systems.

~~~
juhatl
I might've misunderstood what you referred to in this particular case, but you
generally don't need to even have Erlang installed on the target machine to
ship Elixir (and/or Erlang) applications. Tools like Distillery make it easy
(a boolean config value) to include the ERTS with your deployment, leaving you
with a single .tar.gz to move to your target environment. The only caveat here
is that the build machine and target machine architectures must match.

~~~
freedomben
Very interesting, thanks! I'll definitely be looking into Distillery a little
closer.

------
briandear
As a Rubyist, I was about to jump on the Crystal train, but then server-side
Swift started getting really good. Crystal is awesome, but I feel it’s not
solving any problem particularly better than Swift. With Swift being
compilable even on a Raspberry Pi and of course Linux and Mac, it seems like
Crystal is now a solution in search of a problem. Definitely a great language,
but probably destined to be relatively niche in production contexts simply
because the community is so small and it’s loosely competing against the
rapidly maturing Swift world.

~~~
jrs95
Is server-side Swift really that good now? I’m in the market for a new
statically typed language, and it was looking like Java still might be my best
option...and I’m really not a huge fan of that.

~~~
Scarbutt
Besides the JVM and its langs, others with big communities are golang and
typescript.

~~~
riku_iki
There is also MS stack with C#.

------
daniel-levin
I think this thread has no shortage of criticisms, but too many are
uninformed. The blog post doesn't mention these important things:

 _Package management_ (from [0]): "Crystal libraries are packed as Shards, and
distributed via Git without needing a centralised repository. Built in
commands allow dependencies to be easily specified through a YAML file and
fetched from their respective repositories."

 _Concurrency_ (from [0]): "Crystal uses green threads, called fibers, to
achieve concurrency. Fibers communicate with each other using channels, as in
Go or Clojure, without having to turn to shared memory or locks."

[0] [https://crystal-lang.org/](https://crystal-lang.org/)

------
cyberferret
I've been dabbling with Crystal a bit lately, and have been impressed with it,
especially the speed and low memory footprint. Still only developing small
utility app with it at the moment [0] but eventually might look at creating
production API servers with it.

[0] - [http://devan.blaze.com.au/blog/2017/10/28/racing-along-
build...](http://devan.blaze.com.au/blog/2017/10/28/racing-along-building-a-
telemetry-system-using-crystal-rethinkdb)

------
enraged_camel
I used to like OO, but ever since I got a taste of functional programming with
Elixir, I haven’t looked back. It’s a very elegant language and an absolute
joy to program in.

~~~
MuffinFlavored
What are your thoughts on Go/Rust? Node.js? Node.js with Typescript?

~~~
dnautics
Typing is kind of a pain in the butt, it does buy you I would say 20% but when
your paradigm is "code the happy path, let it fail if there's a screwup,
you'll find it and fix it in another release, it won't bring down the server,
the world will still be there", runtime typing errors aren't horrible.
Dialyzer for erlang/elixir is quite nice and brings the delta even lower, if
you must need type analysis.

Go's type system is atrocious, try multiplying a time interval by an user-
input integer. You can't do it. That aside, Go is a fairly good programming
language, and I especially like using it for CLIs since there's cross platform
guarantee.

~~~
barrongineer
I dunno, to me it makes perfect sense that you would need to convert it to a
duration.

time.Second * time.Duration(userInputInt)

~~~
dnautics
the units on that would be time squared. As a scientist, this makes my brain
explode.

[https://en.wikipedia.org/wiki/Dimensional_analysis](https://en.wikipedia.org/wiki/Dimensional_analysis)

For the record, at least two languages, F# (which is statically typed) and
Julia (which is strongly typed) do this 100% correctly

------
moomin
From a recovering OO developer: one of the things I find funniest now is that
OO is constantly treated as a plus in these “why I picked” posts, while I’ve
long concluded that the cost of this easy polymorphism is way too high.

------
rishav_sharan
For those who are interested in Crystal, these are some of the major features
currently in dev: 1\. Windows support 2\. Parallelism 3\. New Immix GC

Crystal is going to be an insane language in a year's time.

------
INTPenis
Is this the language of 2018?

Will this join the ranks of Julia, Rust, Go and the others?

I'm skeptical because the last few years I've seen these trends come and go on
HN. They do nothing but scatter my focus. I've learned to ignore them now
though.

------
ryanx435
Why is Fibonacci the standard performance test? I want to know how it runs on
complex objects with complex computations.

Also this entire article seems to be content marketing for crystal.

And, oh, btw, crystal isn't windows compatible.

~~~
jjtheblunt
If it runs in Ubuntu it runs in WSL, yes?

~~~
kodablah
That's not generally considered Windows compatible. That's Windows 10
compatible and misses many Windows features/APIs.

------
rbosinger
I would love to see Crystal take off but it seems like some decent
funding/backing and then some time to settle from there will be needed. I
seriously hope that can happen; but, who knows.

------
ilurkedhere
Thanks. Next time I write a Fibonacci function, this will surely be a great
resource.

------
emmelaich
Maybe it's because I learned Ruby after Perl and Python (and C and C++ for
that matter), but I find it too quirky.

e.g. These three expressions have three different values:

    
    
       1 + 2 
          + 3 + 4
    
       (1 + 2 
            + 3 + 4)
    
       1 + 2 + 
           3 + 4
    

3, 7 and 10 respectively. Because newline is equivalent to semicolon and Ruby
allows void expressions.

I just tested Crystal and it does the same.

~~~
vidarh
Newline is _not_ equivalent to semicolon (or your last example would fail),
but Ruby treats a newline as terminating an expression if in a production in
the grammar where terminating the expression is valid.

In terms of "void", Ruby has no such thing - every expression has a value,
though that value can be nil (and somewhat confusingly "( )" is an expression
that evaluates to nil).

So your first example is two expressions, and evaluates to 7 when the whole
thing is evaluated, not 3 - if you evaluate it in irb etc. you might see 3
because the first expressions is itself complete, and so will cause output.

Your second example is a list of the expressions "1 + 2" and "3+4", which
evaluates to 7, because as for the first example, the last expression
evaluated is what will be returned outside of a read-eval loop like in irb.

The last example is a single expression because "1 + 2 +" is a syntax error on
its own.

I can understand you find it quirky, but you generally only need to consider
that if you want to break up an expression you need to ensure it does not
stand on its own.

~~~
emmelaich
OK, newline is not equivalent to semicolon but I could be forgiven for
inferring that from the documentation. e.g. "an expression is terminated with
a newline or semicolon"
([https://www.cs.auckland.ac.nz/references/ruby/doc_bundle/Man...](https://www.cs.auckland.ac.nz/references/ruby/doc_bundle/Manual/man-1.4/syntax.html#program))

Re "void" I'm referring to _rubocop_ warnings like "Operator + used in void
context."

Here's another oddity; irb behaves differently for some expressions.

    
    
        not_defined = "#{not_defined}"
        => ""
    

:-/

Anyway, the parens things bothers me the most. In most (all?) other languages,
putting parens around a multiline expression would make it one atomic
expression.

~~~
vidarh
Yeah, I understands it's confusing. The Ruby grammar does have some dark
corners like that where the side effect of trying to make it possible to make
things as free of punctuation as possible causes weirdness elsewhere.

Putting parens around it _does_ make it one atomic expression though, it's
just that in effect the "operators" are ;/lf, and these "pseudo-operators"
return the value of the last sub-expression, and binds tighter than parens,

I can't actually remember seeing any one relying on grouping expressions with
(), though, which is probably just as well.

(I'm sure someone will now come up with an "essential" use of it)

------
ifightcrime
I’m confused. How does mypy make python slower? I thought it was “compile
time” and doesn’t affect runtime.

~~~
deathanatos
I'm not sure what to make of that statement either. It is like saying running
a linter on your code makes it slower. It just doesn't make any sense.

If the author argued that _annotations_ made the code slower… maybe. CPython
exposes them on, e.g., __annotations__, so, _conceivably_ that would slow down
function creation and consume more memory, but my guess would be it would be
such a tiny difference that would be vastly outweighed by the benefits of
having annotations.

------
jpgvm
Another language worth considering is Kotlin.

You get the JVM ecosystem with a modern syntax. It's a higher investment then
Nim/Crystal but also has higher payoff due to existing libraries and peak
performance of the JVM.

~~~
iLemming
Kotlin is a language that was created simply to lock you in using Jetbrains
products. If you like JVM - Clojure is a perfect fit.

~~~
jpgvm
I'm not going to list all the ways Kotlin is superior to Clojure for real
development but suffice to say the two aren't really comparable when you have
a team of programmers with heterogenous backgrounds.

However I do want to note that Kotlin having great IDEA integration is not
lock-in. You can (and I sometimes do) code Kotlin + gradle with a pretty
vanilla vim setup.

Clojure has one big advantage and it's also it's biggest weakness. It's a
Lisp. I love Lisp's but I would never want to use one in a professional
setting.

~~~
lispm
You can use Lisp in professional settings. Lisp has been used successfully in
teams from 2 to 100 people with source-code bases of upto 10 million lines.
The usual things apply: a good team, a good development environment, tests,
documentatiom, etc.

------
stealthmodeclan
When i chose a programming language, here is what i look for:

1\. Amount of things I've to learn to completely utilized 100% features of the
language.

2\. Availability of well supported packages for stuff like elastic search,
stripe, postgres etc...

3\. Ease of making a function asynchronous. I love how go routines make it
trivial and hated pythons way.

4\. IDE and debugger: I am using goland now, and it is magical.

GO has provided me everything, finally i can focus on building my product.

------
mromanuk
Running the same algorithm in my system, C/C++ with -O3 are almost equivalent,
and Crystal too:

Clang C++ 1.069s

C++ 4.2.1 1.075s

Crystal 1.082s

But using a different algorithm (non–recursive) in C++
([http://rextester.com/discussion/OGIF47151/fibonacci](http://rextester.com/discussion/OGIF47151/fibonacci))
you can achieve

C++ 4.2.1 0.005s

someone should port it to Crystal :)

------
fiatjaf
We support Crystal at [http://fiatjaf.alhur.es/module-
linker/#/crystal](http://fiatjaf.alhur.es/module-linker/#/crystal)!

------
st1ck
I'd suggest giving a try to data types + type classes as opposed to OOP. E.g.
Haskell and Rust. Or OCaml, F# or Scala for combination of OOP + functional
style.

------
_RedPanda
>CPython 3.6.5 128.172

Am I reading this correct? it took CPython 128 seconds for a simple Fibonacci
test? Can anybody explain to me why this is so slow?

~~~
icebraining
Function calls in CPython are not very fast, and the test makes over 850
million calls.

