

Meet Crystal language in its birthday. It is compiled and has ruby like syntax - carohadad
http://crystal-lang.org/2013/09/04/happy-birthday-crystal.html

======
gregwebs
This is really interesting. A hypothesis I have on Ruby is that people
attribute dynamic typing to it being a productive language, but that Ruby is
actually productive for other reasons, _in spite of_ being dynamically typed.

With Crystal, at least when it matures a bit more, this hypothesis could be
tested.

There are very logical reasons why dynamic typing at first appears better than
static for Rubyists, that I think don't hold up as well after you scratch the
surface:

* many Rubyists came from Java, and that kind of typing does slow you down. You need a modern type system with at least local type inference (Crystal seems to have global type inference)

* dynamic typing does actually help develop things more quickly in some cases, definitely in the case of small code bases for newer developers. A developer only has to think about runtime. With static typing a developer also must think about compile-time types, which takes time to master and integrate into their development. The relative payoff of preventing bugs grows exponentially as the complexity of the code base increases and at least linearly with size of the code base.

~~~
pjungwir
There is a Ruby with static typing---it's called C#. Seriously, even though my
background is all in non-MS stacks, I did some C# work a few years ago and
found the language surprisingly nice, and with a lot of the features I love
from Ruby. It even has type inference like Crystal. (The _libraries_ are not
nearly as clean/consistent as Java though.) For folks in the Ruby/Python/Java
world, it's something to look at.

~~~
icambron
As someone whose last 8 years of professional work have been largely split
between...C# and Ruby, I have to disagree. I _do_ agree about C# being quite
nice; it's all the things Java should be but isn't. And it's evolving quickly
in all the right ways.

But it isn't really like Ruby with static typing. The language isn't Rubyish
(no mixins or blocks, for example). It makes you put everything in a class, a
la Java. It can verbose and is occasionally downright clunky (though
syntactically it's categorically slicker than Java). The .NET ecosystem
doesn't have the Ruby characteristic of lots of small, fast-evolving libraries
that are easy to use. In fact, the C# open source ecosystem is kinda poor in
general and not a huge part of most developer's lives, whereas Ruby's
ecosystem is vibrant and an integral part of its coding culture.

Another way to put all that is that if C# were purely dynamically typed, it
wouldn't feel anything like Ruby.

I do see what you're saying: LINQ feels like a static (and lazy!) version of
Ruby's Enumerable module, the lambdas look similar, C# actually does have
optional dynamic typing, and C# is increasingly full of nice developer-
friendly features. In general, I'm a fan. But switching between them doesn't
feel like just a static/dynamic change.

~~~
thecoffman
Interesting - I found the lambda syntax of LINQ to be quite "Ruby like"

    
    
      someList.FindAll(i => i < 2)
        .Select(i => i * 2)
        .GroupBy(i => (i % 2) == 0 ? "even" : "odd")
    

Is about as close to Ruby's Enumerable as I've found in a
mainstream/enterprise language (unless you include Scala).

~~~
icambron
Yeah, I mostly of agree with that and sort of said so in my post. Not sure it
has much impact on my central point though.

One really key difference with LINQ is that it doesn't produce arrays (or
dictionaries, as in your example); it produces Enumerators, which you then
have to do call toList() or toDictionary() on. That laziness is actually an
awesome feature and my favorite thing about LINQ, because it can massively
improve performance by shortcutting work and not creating intermediate arrays.
You can even work on infinite sequences with it. Besides performance, it's
just tastier. It's so great I actually wrote a Ruby library to imitate it:
[https://github.com/icambron/lazer](https://github.com/icambron/lazer)

~~~
VexXtreme
Is LINQ really fast/performant though? Wouldn't the above expression cause
three sequential loops to run?

One of the biggest performance issues I've seen with modern .NET code is
people abusing LINQ and lambdas. Chaining functions like this is most
decidedly not fast. I once wrote a library that had do do some heavy signal
processing on large data sets, and since I wanted to ship the first version as
soon as possible, I just used LINQ in a lot of functions to save time. It
wasn't very performant so later I rewrote most of the functions to use
standard native code such as loops for iteration, hashmaps for caching and all
sorts of improvements like that. I completely got rid of LINQ in that version
and for many functions the runtime went down from something like 500ms-1000ms
to microsecond area.

So sure, LINQ makes development fast and it's very nice to be able to write
code such as .Skip(10).Take(50).Where(x => ...). On most web projects, it
won't make a huge difference. I've seen Rails "developers" use ActiveRecord in
such a way that they would create double and triple nested loops and then hit
the database multiple times by using enumerable functions on ActiveRecord
objects without realizing how this works, what's going on behind the curtains
and so on. I've seen .NET devs do similar things using EntityFramework.

So yeah, it's convenient and all, but it can also be very dangerous when used
by someone who doesn't understand the fundamentals behind these principles.

~~~
icambron
> Wouldn't the above expression cause three sequential loops to run?

No, it wouldn't; that's the really important point about LINQ I was, clumsily,
trying to express above [1]. Take this admittedly totally contrived example:

    
    
      someList
        .Where(i => i % 2 == 0)
        .Select(i => i + 7)
        .Take(5)
    

This is _not_ equivalent to a bunch of sequential loops. What it is is a bunch
nested Enumerators. Here's how it works. It gets the list's Enumerator, which
is an interface that has a MoveNext() method and a Current property. In this
case, MoveNext() just retrieves the next element of the list. Then Where()
call wraps that enumerator with another enumerator [2], but this time its
implementation of MoveNext() calls the wrapped MovedNext() until it finds a
number divisible by 2, and then sets its Current property to that. _That_
enumerator is wrapped with one whose MoveNext() calls underlying.MoveNext()
and sets Current to underlying.Current + 7. Take just sets Current to null
after 5 underlying MoveNext() calls.

So all that returns an enumerable, so as written above, it actually hasn't
done any real work yet. It's just wrapped some stuff in some other stuff.

Once we walk the enumerable--either by putting a foreach around it or by
calling ToList() on it--we start processing list elements. But they come
through one at a time as these MoveNext() calls bring through the list items;
think of them as working from the inside out, with each MoveNext() call asking
for one item, however that layer of the onion has defined "one item". The item
is _pulled_ up through the chain, only "leaving" the original list when it's
needed. The entire list is traversed at most once, and in our example,
possibly far less: the Take(5) stops calling MoveNext() after it's received 5
values, so we stop processing the list after that happens. If someList were
the list of natural numbers, we'd only read the first 10 values from the list.

Now, those nested Enumerator calls aren't completely free, but they're not bad
either, and you certainly shouldn't be seeing a one second vs microseconds
difference. If you craft the chain correctly, it's functionally equivalent to
having all of the right short circuitry in the manual for-loop version, and
obviously it's way nicer.

So why are you seeing such poor perf on your LINQ chains? Hard to say without
looking at them, but a few of pointers are: (1) Never call ToList() or
ToDictionary() until the end of your chain. Or anything else that would
prematurely "eat" the enumerable. (2) Order the chain so that filters that
eliminate the most items go at the end of the chain, similar to how you'd put
their equivalent if (...) continue; checks at the beginning of your loop body.
(3) Just be cognizant of how LINQ chains actually work.

[1] In the example in the parent, FindAll isn't actually a LINQ method, so
there is one extra loop in there. Always use Where() if you're chaining; use
FindAll() when you want a simple List -> List transformation.

[2] A detail elided here: each level actually returns an Enumerable and the
layer wrapping it does a GetEnumerator() call on that.

~~~
VexXtreme
Thank you for this awesome explanation, it really clarifies how Enumerable
methods work. I guess this is the real reason behind deferred execution.
Still, I can't help thinking there is a big overhead involved with using such
methods. We did some benchmarks in the past and the code that does the same
thing manually always ended up being much faster.

The nice thing about Enumerable methods is that they can significantly speed
up development and most projects won't suffer for it. However, for speed
critical code it's probably not the best tool in the box.

------
theseoafs
I was very excited by this when I read the description, because a compiled
language that looks like Ruby is exactly what I've wanted. Unfortunately I'm
not super excited by the quirks of the implementation. For example:

    
    
        if some_condition
          a = 1
        else
          a = 1.5
        end
    

If I'm working in a compiled and typed language, the _last_ thing I want is a
language that automatically gives union types to variables. As far as I'm
concerned, the type inference should fail at this point. In the above example,
now I'm forcing the compiler to maintain a union which is going to have a
pretty significant overhead on every computation the variable `a` is involved
in.

~~~
hrjet
But isn't that how type inference works in most statically typed languages?

For example, in Scala: val x = if (some_condition) Employer else Employee

If Employer and Employee both derive from Person, then x will be of type
Person. The run time uses dynamic dispatch to figure out how members are
accessed from x.

If you want to constrain x, you need to specify the type explicitly.

~~~
justinpombrio
Your example shows a different kind of behavior than the parent's example. A
union type (t1 \/ t2) is a type that says "either this value has type t1, or
it has type t2".

In your example, the type of x is not (Employer \/ Employee), it is their
shared superclass - Person. The analogous example would be if

    
    
        val x = if (some_condition) Employer else Employee
    

succeeded _even though Employer and Employee did not share a superclass_. Very
few languages use union types - Typed Racket comes to mind, and Algol
apparently did too.

~~~
asterite
But actually in Crystal you will have the same behaviour:

    
    
      class Person
      end
    
      class Employer < Person
      end
    
      class Employee < Person
      end
    
      x = some_condition ? Employer.new : Employee.new
      # x is a Person+
    

This is not said in the "happy birthday" article (or anywhere else, IIRC).

In the beginning we typed x as Employer | Employee. But, as the hierarchy grew
bigger compile times became huge. Then we decided to let x be the lowest
superclass of all the types in the union (and mark it with a "+", meaning:
it's this class, or any subclass). This made compile times much faster, and in
most (if not all) cases this is what you want when you assign different types
under the same hierarchy to a variable.

What this does mean, though, is that the following won't compile:

    
    
      # Yes, there are abstract classes in Crystal
      abstract class Animal
      end
    
      class Dog < Animal
        def talk
        end
      end
    
      class Cat < Animal
        def talk
        end
      end
    
      class Mouse < Animal
      end
    
      x = foo ? Dog.new : Cat.new
      x.talk # undefined method 'talk' for Mouse
    

That is, even though "x" is never assigned a Mouse, Crystal infers the type of
"x" to be Animal+, so it really doesn't know which types are in and considers
all cases.

Again, this is most of the time something good: if you introduce a new class
in your hierarchy you probably want it to respond to some same methods as the
other classes in the hierarchy.

~~~
theseoafs
Well, the happy birthday article has a section on "union types", and that code
block has the comment "# Here a can be an Int32 or Float64". I just assumed
that this meant a had the type Int32 | Float64. If the language doesn't
actually have union types, then the article should probably be edited to
reflect that (because it's very misleading on this issue).

~~~
asterite
It has union types. Right now if you do 1 || 1.5 it gives you Int32 | Float64.
If you do Foo.new || Bar.new, and Foo and Bar are not related (except they
both inherit from Reference), then you get Foo | Bar. If Foo and Bar are
related by some superclass Super, then you get Super+.

If you do:

a = [1, 'a', 1.5, "hello"]

you get Array(Int32 | Char | Float64 | String)

In a way, the Super+ type is a union type of all the subtypes of Super,
including itself, but just with a shorter name.

------
lloeki
The one that caught my eye is that the macros are disappointing: they merely
feel like (not even glorified) syntactic sugar for an _eval_ wrapped in a
function.

Hint: if I'm manipulating/interpolating a string, it's an eval, not a (lisp-y)
macro.

------
saljam
I guess it's also worth mentioning Mirah, a JVM language heavily inspired by
Ruby. Unlike JRuby, it compiles down to byte code ahead of time and needs not
support libraries.

[http://www.mirah.org](http://www.mirah.org)

------
vinceguidry
I'm putting my faith in Ruby. It might take 10 years, but eventually the
performance will resemble C's. It's basically a compile-to-C language right
now as it is. There's just a whole bunch of inefficiencies in the
implementation. Once they get ironed out, we'll finally be able to have our
cake and eat it too. One language to rule them all.

~~~
octo_t
No it won't. There are so many problems with trying to get a dynamic language
like ruby to be in the same order of magnitude of performance as C.

Garbage collection, inlining virtual calls etc.

~~~
melling
Go has garbage collection and it's in the same order of magnitude as C. In
fact so are Scala and Java. So, we can at least stop blaming gc.

~~~
andrewvc
Yes, but neither language is nearly as dynamic as ruby. That's where the costs
come in. Method calls in ruby are comparatively glacial in speed.

~~~
vinceguidry
That's why the ten years.

~~~
lispm
after ten years, square wheels will suddenly be round?

~~~
haar
If you've been rotating them against a surface in that time period; then
they'll certainly be a lot more round than when they started.

------
cmollis
Dynamic typing is simply sugar. It's a bit misleading (particularly for newer
developers) that you don't have to think about types just because you don't
have to declare them. Automatic coercion, etc, is really only useful if you're
a more experienced developer because for the most part, you keep the type
information in your head.

That said, python is the most productive language I've used so far but type
management is really just one part of that.

------
nwmcsween
You cannot have full classical OOP with C like performance. It won't happen
without a big fat JIT runtime and more optimization than put into hotspot.

~~~
qznc
Yes, you can. You might have heard of C++.

~~~
swift
Uhh, "classical OOP". Like Smalltalk. What C++ calls "OOP" has its place, but
it's not the same thing.

------
hcarvalhoalves
Interesting. I really liked the solution for writing C bindings.

Given the syntax looks similar, could it run Ruby source, unaltered?

~~~
spoiler
Some of it, I guess. Sometimes it would break due to "type differences," I
guess.

------
tbrock
This is great. Now if we need parallelism we can choose Rubinius and if we
require raw speed we can choose Crystal (when it's done) instead of jumping on
the JRuby bandwagon.

I respect Charles Oliver Nutter but Java is something I want less of in my
life. This seems like a great alternative for people seeking performant Ruby
interpreters.

~~~
untothebreach
This isn't a Ruby interpreter

------
ddfreyne
There is a typo: foo2.ord in the first class Foo(T) example should be
foo2.value.ord.

~~~
asterite
Thanks, I just fixed the example.

------
spoiler
It would be really cool if one could write binary extensions/gems for Ruby
with Crystal somewhere down the line... far down the line, probably.

~~~
asterite
Yes, it's one of our dreams too. Right now you can do:

    
    
      # In a foo.cr file
      fun init_foo = Init_foo : Void
        puts "Init foo! :-)"
      end
    

Compiling it:

    
    
      bin/crystal foo.cr -o foo.bundle
    

Then in Ruby:

    
    
      $ irb
      irb(main):001:0> require "./foo"
      Init foo! :-)
      => true
    

But more complex things don't work right now (because of the initial
implementation of the GC, which needs to be turned off for this case).

So, yes, we are not that far from allow you to write Ruby extensions in
Crystal.

We'd also like to write Objective-C code that way, and also Erlang extensions.

------
ddebernardy
One of Ruby's benefits is UTF-8 support for strings. C-strings make it seem
it's absent. Or did I missunderatand?

~~~
asterite
The current String implementation is just a temporary one. UTF-8 will be
there.

------
grandalf
Looks very interesting. I am intrigued by the idea of Ruby having type hinting
and optimizations based on hints.

------
continuations
Can it use Ruby libraries? People use Ruby mostly because of its ecosystem.

~~~
vinceguidry
It appears to have nothing to do with Ruby except syntax similarities.

------
blah32497
Another language with un-googleable name.

~~~
colanderman
Can't be worse than Why. (Go ahead, I dare you to find it!)

~~~
andrewflnr
Wow. You have to quote "the why programming language" before you even get a
trace. It's not even in wikipedia's alphabetized list.

------
NDT
How is this different from Ruby?

~~~
scotth
It's compiled and type-checked.

~~~
ser0
Which also implies performance benefits over Ruby. I think another way to view
it is Go with Ruby syntax (although Go has its own runtime overhead). Their
introduction document [1] is a pretty concise overview.

I think it's an interesting project. But not being a Ruby focused coder these
days, I can't see myself choosing this over other compiled languages at this
point.

[1]
[https://github.com/manastech/crystal/wiki/Introduction](https://github.com/manastech/crystal/wiki/Introduction)

