
Crystal Language - necrodome
http://crystal-lang.org/
======
davexunit
I don't understand why Ruby's syntax is seen as so elegant. It's ambiguous and
a nightmare to parse.

[http://programmingisterrible.com/post/42432568185/how-to-
par...](http://programmingisterrible.com/post/42432568185/how-to-parse-ruby)

~~~
Lrigikithumer
Elegant to program with. Not write a parser for.

~~~
davexunit
An ambiguous syntax is _not_ elegant to program with. Is this a variable
reference? Is it a method call? I dunno!

~~~
jacques_chester
> _Is this a variable reference? Is it a method call? I dunno!_

Neither. It's a message sent to an object. The object decides how to respond
to that message.

Caring about the implementation of the message receiver breaks information
hiding. If you have to distinguish between .message and .message(), you
already know more than you _need_ to know to interact with that object.

Every language is confusing until you understand the mental model (even if you
disagree with the utility of that model). If you come to Ruby thinking in
Java/C++/C terms, you'll be unhappy, because the mental model is very
different.

For Ruby, the book to read is _The Well-Grounded Rubyist_ , which makes the
language quite obvious.

~~~
davexunit
I've been programming in Ruby for years, and the inherent ambiguity proven by
the complexity of the parser isn't made better by telling me I'm not thinking
right.

Variables and methods are treated differently. I can't pass arguments to a
variable, I can't even write foo(), but if it were a method I could. I can't
treat variables like any other object because they have different rules
applied.

~~~
jacques_chester
> _I can 't pass arguments to a variable_

I'm confused -- if you're knowingly invoking a method, why are you unsure
whether it's a method or a variable?

I programmed for years in Ruby too. I just accepted a whole bunch of stuff as
mysterious and shrugged. It didn't mean the language was ambiguous, I just
didn't have any idea of what the actual model was.

------
kibwen
Crystal looks like a neat language, and it's fun to see how many entrants
there are in the modern renaissance of scripting languages that compile to
native code. I wish there was some more documentation about it though... for
example, does the bullet point "Never have to specify the type of a variable
or method argument" from the home page imply gradual typing as per Dart and
TypeScript, or is it something else entirely?

~~~
asterite
It has global type inference, there's nothing dynamic in the language. You can
specify type restrictions to allow overloading methods, for example, or doing
multiple dispatch. But in the general case you don't specify types (except for
generic type arguments) and the compiler figures out everything.

~~~
kibwen
I'm curious how possible it is in practice to write large software that relies
exclusively on global type inference. Does the compiler itself go without ever
explicitly specifying a type?

~~~
reagency
GHC does global type inference, and has very sophisticated types.

~~~
kibwen
My point was specifically in regard to the feasibility of omitting the types
in all cases. I see types often in Haskell code, likely because the community
regards that as a best practice (in contrast to Crystal).

~~~
tel
Best practice and it's required to make certain more advanced type features
work.

It's the same with Crystal. Their generics require type annotations.

------
sergiotapia
I'm looking for something beautiful like Ruby but fast like Go. Do you think
Crystal fits this bill?

Also, are there packages/libs/gems for Crystal? What are they called? What do
I google for?

One of the major reasons why I dumped Go is that it's just too verbose and
makes me write too much boilerplate code. I want to sort a collection and I
have to write the same algorithm every single time for every single type. It's
just boring and my time could be better spent elsewhere.

I appreciate the feedback HN!

~~~
CyberDildonics
You definitely should at least look into Julia. I would say it is more elegant
than any other language I've seen while being faster than pretty much anything
except for straight C / C++ / D / Rust (native systems languages).

It should be easy to beat Go in performance and match or exceed Ruby in
elegance and simplicity.

It's Achilles heel(s) right now though are multi-threading and JIT compilation
times (which should be alleviated once caching is implemented).

It sounds really close to what you want. Its package management is even based
off of github. Want to get a package to write out image files?
Pkg.add("Image"). Update all your libraries? Pkg.update()

Over the next 5 years I think it might eat into a lot of the territory of
scripting languages.

Also the generic programming focus means you should never have to write
anything more than a new comparison for your new types.

~~~
zeeone
Julia's 1-based array indexing is counter-intuitive and a turn off.

~~~
CyberDildonics
I've seen people say this but really 1 based indexing should be extremely
trivial to all the other complexities of writing a worthwhile program for
anything more than a one liner.

------
aikah
I'm a go user right now, but I really want to ditch it because I'm in total
disarray with the way the go language is managed and the deafness of the go
team. Been looking at D,Nim and Crystal.

\- D is neat but I'm not interested at all in unsafe stuffs and don't want to
have to debug programs or 3rd party libs that relies on that, I want a safe
language.

\- Nim looks really good, although some features like (foo_bar = FooBar ) are
just disgusting

\- While Crystal is new and libs is non existent, it feels like a good
candidate for the long run. I hope it will have the same concurrency
capabilities as go. Good luck with the language.

~~~
p0nce
D has a memory safe subset (@safe). You could argue that Rust 3rd party libs
rely on unsafe blocks.

~~~
kibwen
The person you're replying to wasn't even talking about Rust. You'd also be
vastly overestimating the amount of Rust libs that need unsafe code. For
example, Rust's most mature web framework, the one powering crates.io, doesn't
use unsafe code at all:
[https://github.com/iron/iron](https://github.com/iron/iron)

~~~
steveklabnik
Crates.io uses Conduit, not iron.

~~~
kibwen
Ah, so it does. :) Fortunately Conduit doesn't use any unsafe either.

------
rtpg
[http://crystal-lang.org/2013/07/13/null-pointer-
exception.ht...](http://crystal-lang.org/2013/07/13/null-pointer-
exception.html)

The Null pointer analysis is great, hope this stuff pollinates some of the
more mainstream languages

~~~
raindev
I don't see a reasons for null to exist in a new language in 2015.

~~~
asterite
How do you model a "zero or one" relationship without null?

Maybe your answer is "with Optional" (or Option, or Maybe). We just choose to
use union types and have "Nil | T" (Nil or T) be the same as "Option(T)" in
other languages.

~~~
raindev
Yes, Maybe (Optional) is the way to go. The difference is, with nil you
basically make every type optional allowing it to have nil as a value.

~~~
asterite
Well, in Crystal Nil is a separate type that can be combined with others. But,
say, a String is always a String, it doesn't implicitly have the Nil type.
Same goes with every other type.

Maybe you are thinking of Java/C#, where reference types can also be null, but
this is not true in Crystal. It's also in a way similar (but not quite) to
Swift, where optional types are different than types that can't be null.

~~~
wtetzner
It's a nice approach, I like it. There is a difference between Option[T] and
(T | Nil) that's worth mentioning, however.

Option[T]'s are composable. For example, let's say we have a "get" method to
get the value for a given key, whose type looks like:

    
    
        get :: String -> (T | Nil)
    

If we were using Option[T]'s, it would look like:

    
    
        get :: String -> Option[T]
    

So let's say we have a map, and want to lookup a key (syntax is made-up):

    
    
        let m: Map[String,(Int | Nil)] = make_some_map()
        let result: (Int | Nil) = m.get("some-key")
    

If result is nil, was the value of the key nil, or was the key not in the map?

With Option[T]:

    
    
        let m: Map[String,Option[Int]] = make_some_map()
        let result: Option[Option[Int]] = m.get("some-key")
    

Here result will either be None, in which case the key wasn't in the map, or
Some(None), which means the value of the key was None.

So there is an observable and potentially useful difference between (T | Nil)
and Option[T].

------
MCRed
I love that we're getting new languages lately, but almost all of them seem to
be ignore the significant new requirement of our age: parallelism &
concurrency.

Specifically, you need lightweight processes and no-shared-memroy
architecture.

While the number of cores on a machine is remaining relatively low, the number
of machines in a system are going up.

Erlang got this right and build a lot of infrastructure around it (OTP) and
while you can't replicate that infrastructure quickly you can get the
fundamentals right.

Simply getting this wrong rules out a lot of languages from consideration
(because why learn a new language that is going to be obsolete, or only chosen
by people who don't understand how to build systems?)

It's a lot easier to get this in when you're new and can make major changes to
the language. Once you start to solidify it would break things- this is why
Go's fake concurrency is a tragedy and a huge missed opportunity.

~~~
seanmcdirmid
> Specifically, you need lightweight processes and no-shared-memory
> architecture.

No you don't. I mean, that's one way to do it, but its not the only way and is
often not an option if you want real performance. CUDA (a language
specifically designed for SIMT parallelism) supports shared memory for good
reasons; the threads are also not lightweight in that way (though they rely on
memory and branch coherence).

~~~
kayamon
It can be argued that shared memory should be explicit-when-needed rather than
everywhere-by-default though.

~~~
seanmcdirmid
It can also be argued the other way, of course. Do you like eating vegetables
or ice cream? It's like garbage collection, some people love it because it
frees them from worrying about allocating and freeing memory. Others hate it
for the same reason.

------
zeeone
Just curious, why Crystal? It looks just like Ruby. What problem(s) are you
addressing with Crystal?

~~~
asterite
We like the way Ruby lets you quickly prototype things, but its performance
isn't very good (it's just good) and it also lacks static type checks (for
example "undefined method '...' for Nil" is a very common runtime error).

So, we are trying to create a language with all the nice aspects of Ruby but
with static checks and better performance. Of course that comes at a price: no
dynamic aspects (no eval, no instance_eval, no methods or classes created at
runtime, no methods redefined at runtime, etc.). But we try to compensate
those with macros and compile-time reflection.

One thing that is definitely not one of our goals is to replace Ruby: every
tool has its place.

~~~
chrisseaton
People talk about how Crystal's performance is better because it's statically
compiled and removes Ruby's dynamic features, but I'm not sure static
compilation is the best way to achieve performance, and I don't think the
dynamic features need to damage performance.

For example, JRuby+Truffle runs Crystal's own sample programs around twice as
fast as Crystal does, without static compilation, and while still supporting
all the metaprogramming and dynamic features of Ruby such as monkey patching,
send, method_missing, set_trace_func, ObjectSpace etc.

[https://gist.github.com/chrisseaton/91c7cbf8f6f4f6ea44bb](https://gist.github.com/chrisseaton/91c7cbf8f6f4f6ea44bb)

However Crystal does start faster, and I'm sure it has lower overhead.

~~~
asterite
Did you compile the Crystal program with the --release flag? That turns on
optimizations. On our machines we see Crystal is faster in this benchmark, it
always takes about 1.6s while JRuby+Truffle reaches 2.38 at most.

~~~
chrisseaton
No, I didn't, sorry!

I don't see that option documented anywhere except the changelog, and one
passing reference in the docs that says it sets the release flag, but neither
say it has any effect on optimisation.

~~~
asterite
Yeah, we need to document this better, sorry!

~~~
kibwen
Get used to this, watching people run benchmarks after having forgotten to
compile with optimizations is basically a meme in the Rust community. :)

~~~
asterite
I don't think it's a big deal, benchmarks are just toy programs. Once you
learn about the `--release` flag you never forget it for production-ready
code.

C has -O3, why isn't it the default? Because it takes a lot more time to
compile. So I think no optimizations by default is the best choice. And I
think Rust should do the same, you will be compiling more things in non-
release mode than in release mode.

~~~
kibwen
Compiling without optimizations is indeed the default in Rust, but we have
seen such an unbelievably large number of people not realize that a
`--release` option exists that there have been several debates regarding
whether or not this should be changed. Instead we've stepped up our
documentation, we've made the package manager tell you which mode it's
compiling in, and we've made Cargo put the finished binaries in either a
clearly-named `debug` or `release` directory... and yet still they crash upon
our walls, waving benchmarks where Rust is seemingly three times slower than
Ruby.

It may seem obvious to you and I that compilers have optimization levels, but
consider how many people for whom Java, with no optimization level flags, is
their only exposure to a manually-invoked compiler.

------
singularity2001
Awesome! Did you consider synchronizing your syntax with Mirah / typed ruby?
Also what's your opinion on the Vala/Gini toolchain?

------
_RPM
How long did it take to become self hosting? I checked the first tag on GitHub
and it was still implemented in Crystal. Did you originally implement it in C
or another language?

~~~
asterite
It took about one year. We originally implemented the compiler in Ruby and
because of its similarity with Crystal (or the other way around ;-)) porting
it to Crystal was pretty easy in the end.

There's this tag:
[https://github.com/manastech/crystal/releases/tag/ruby](https://github.com/manastech/crystal/releases/tag/ruby)

You can read about the bootstrapping moment here: [http://crystal-
lang.org/2013/11/14/good-bye-ruby-thursday.ht...](http://crystal-
lang.org/2013/11/14/good-bye-ruby-thursday.html)

------
trengrj
I've been looking at the benchmarks
[https://github.com/kostya/benchmarks](https://github.com/kostya/benchmarks)
and am pleasantly surprised about the speed. It definitely smokes Ruby, but
also is usually faster than Go. I know that all benchmarks are relative but
Crystal seems a great language from a performance viewpoint.

~~~
ezdiy
Indeed it's becoming quite promising (using crystal already for few pet
scripts which were too slow even for rubinius). Where it's most lacking at the
moment is gc - it uses stop-world off the shelf boehmgc which is ok but not
exactly great for memory heavy tasks.

~~~
kephra
imho, starting a new language with Boehm GC is a very bad design choice. It
means, that one just allocates memory, and does not care for managing it. Even
worse, it prevent linking any library, e.g. a 2nd thread running Lua+C, that
cares for its own memory, because Boehm GC runs over the complete memory, not
only the one the language has to manage.

You basically need 3 types of memory: First for the objects in your language,
2nd for foreign light weight objects, where you only know a pointer, and 3rd
for foreign heavy weight objects, that gets their memory from your GC.

> it uses stop-world

A fully concurrent GC is impossible, if variables are mutable. Regardless how
tricky your GC delays the problem, there will come a point, where it has to
stop all threads to collect the edge cases. This creates the GC dilemma,
because currently only number of cores and amount of memory becomes cheaper,
while single core performance stayed same for nearly 15 years.

~~~
asterite
In the beginning Crystal didn't free memory. We needed a GC and Boehm was a
super easy way to get that. It worked out of the box with very little effort.

Eventually we can write our own GC or use another one. It's only a matter of
time. But right now there are more important things, we think: finishing the
language rules, stabilizing things, fixing bugs, completing the standard
library and writing documentation.

Nothing is set in stone in a language, things can always evolve and improve.

------
agluszak
Wow! Ruby was the first programming language I learnt and I still love it.
Crystal is going to add features I've always missed: static typing and
compilation. Thank you guys, it will be awesome!

~~~
sir_akshay
You missed compilation?

~~~
singularity2001
He probably meant the results of compilation: superfast binaries ;)

~~~
espadrine
In that case, what he missed was AOT optimization :)

Many Lisps have had compilation to binaries. But having a binary doesn't imply
being faster than an interpreted language.

Theoretically, a JIT optimization can end up with faster code than an AOT
optimizer can produce, thanks to runtime profiling.

In fact, I'd be interested in seeing benchmarks, which Crystal is probably not
ready to share, but may have conducted:
[https://github.com/manastech/crystal/blob/master/samples/fan...](https://github.com/manastech/crystal/blob/master/samples/fannkuch-
redux.cr)

Edit: this[0] seems to indicate that the optimizations are in the same
ballpark as Go and D, which is really impressive.

[0]:
[https://github.com/kostya/benchmarks](https://github.com/kostya/benchmarks)

~~~
masklinn
> Edit: this[0] seems to indicate that the optimizations are in the same
> ballpark as Go and D, which is really impressive.

not sure about D, but Go is known to have very little in the way of
optimisation, that's a big factor in its speedy compilation.

------
AKifer
At first look, I like it. Would be great if we can build a ruby-to-crystal
bridge so all the ruby goodness can run on top of it without requiring
programmers to learn a whole new language. No GIL, faster than ruby but
syntactically close enough, the dream.

~~~
ezdiy
Current channels implementation uses fibers, ie everything runs on single
thread and only stack is switched. There is no GIL.

If you want true multithreading, simply use pthreads like you would in C. Just
make sure to mark gc roots of new thread stacks (gc is thread aware). But this
also comes with a lot of headaches as you're now responsible to synchronize
everything and manage mutexes by hand.

------
Nob-suz
I made a Crystal Language Web/Git information collection pages.
[https://github.com/nob-suz/crystal/wiki/1.-Read-
First-%28gen...](https://github.com/nob-suz/crystal/wiki/1.-Read-
First-%28general%29)

See Wiki 1. to 9. If you enjoy your first try for Crystal Language, it's nice.

------
zero_iq
Wow, I started designing a language called Crystal in the 90s, which a) looks
similar to this, and b) I'm pretty sure I even bought the domain crystal-
lang.org at one point, but I never did anything with it! I had a very similar
logo too :)

------
keypusher
Python is to Nim as Ruby is to Crystal.

~~~
singularity2001
More like Crystal could become to Ruby what RPython is to python

~~~
masklinn
That makes no sense. RPython is not a general-purpose development language,
it's a strict subset of Python for writing VMs. From what I understand Crystal
aims to be a general-purpose statically-typed native-compiled language with a
syntax similar to Ruby in much the way Nim aims to be a general-purpose
statically-typed native-compiled language with a syntax similar to Python.

Neither Nim nor Crystal exist solely to write VMs and auto-implement JITs, and
programs in neither is expected to run unmodified on a VM for the inspiration
language.

------
singularity2001
Now someone convince Matz to reimplement Ruby in crystal, add optional typing
to CRuby, and you have won big time.

~~~
atmosx
Actually, that's already happening[1]. Also note that there is a library for
"ruby contracts[2]" (which I didn't try yet, but I'm very eager to do so)
which could give the same result as a statically typed language.

[1] [https://www.omniref.com/blog/blog/2014/11/17/matz-at-
rubycon...](https://www.omniref.com/blog/blog/2014/11/17/matz-at-
rubyconf-2014-will-ruby-3-dot-0-be-statically-typed/)

[2]
[http://egonschiele.github.io/contracts.ruby/](http://egonschiele.github.io/contracts.ruby/).

------
westoque
Now. I just can't wait till Rails or something similar gets ported to Crystal.

------
elcct
Please don't be inspired by Ruby, please... aaaah god dammit!

