Crystal – A Ruby-esque programing language

Crystal Clear

© Lead Image © Lukas Gojda, 123RF.com

© Lead Image © Lukas Gojda, 123RF.com

Article from Issue 185/2016
Author(s):

Crystal is an open source project that seeks to combine the best of two worlds: the simplicity of a language syntax similar to Ruby and the speed and capabilities of the LLVM platform.

In the fall of 2012, Argentinian Ary Borenszweig implemented his Crystal project [1] as a "programming language for people and computers." This sentence probably best expresses what this language sets out to combine: the simplicity and elegance of a Ruby-esque language syntax with the efficiency and speed benefits of compiled languages such as C.

The driving force behind the development of Crystal in recent years has been the Argentine software consulting company Manas [2], which is where Borenszweig works. As of this writing, some 100 volunteers are pushing forward with the project on GitHub [3]. You can look up all of the language's functions in a GitBook [4]. Newcomers can take their first steps without installing anything by entering code into a simple web-based Crystal editor and compiler [5].

Benchmark

The script in Figure 1 runs the Ruby code from Listing 1 as a simple benchmark. It includes a loop containing a multiplication. From the time measured at the start and end of the program, a simple subtraction gives you the execution time. Ruby 1.9.3 takes around six seconds, or around four seconds for JRuby. On the website [5], the same script takes only around 0.3 seconds (Figure 2).

Listing 1

Benchmark

§§nonumuber
01 time1 = Time.now
02 (0..100000000).each do |i|
03   i*16
04 end
05 time2 = Time.now - time1
06 puts time2.to_f
Figure 1: At the Crystal website, you can try simple scripts.
Figure 2: LLVM helps compile and execute the script.

After installing Crystal on your computer (see the "Installation" box), you will find the crystal command-line tool. To execute the Ruby script from Listing 1, all you need to do is enter:

Installation

Right now, Crystal is not found in the official repositories of major distributions.You can add the repository and install Crystal on your system, then install a number of additional libraries needed by Crystal at run time [6] as follows:

Debian and Ubuntu:

curl http://dist.crystal-lang.org/apt/ setup.sh | sudo bash
sudo apt-get install crystal
sudo apt-get install libbsd-dev libedit-dev libevent-core-2.0-5 \
  libevent-dev libevent-extra-2.0-5 libevent-openssl-2.0-5 \
  libevent-pthreads-2.0-5 libgc-dev libgmp-dev libgmpxx4ldbl \
  libpcl1-dev libssl-dev libxml2-dev libyaml-dev libreadline-dev

Red Hat and CentOS:

curl http://dist.crystal-lang.org/rpm/setup.sh | sudo bash
sudo yum install crystal
sudo dnf -y install gc-devel gmp-devel libbsd-devel \
  libedit-devel libevent-devel libxml2-devel libyaml-devel \
  llvm-static openssl-devel pcl-devel pcllib-devel readline-devel

Some packages automatically resolve dependencies against other packages.

$ crystal bench1.rb

On the test system, the benchmark program only took 0.23 seconds to run the program code; thus, it ran 30 times faster than the plain vanilla Ruby version.

Crystal does not interpret the Ruby script; first, it compiles the script using the LLVM infrastructure (see the box "Victory for LLVM Compiler Infrastructures") to create native code. Crystal generates pure C code, which is strictly typecast – in contrast to Ruby code.

Victory for LLVM Compiler Infrastructures

At the University of Illinois, developers Vikram Adve and Chris Lattner, launched a study project in 2000 to implement a new compiler infrastructure. Their Low-Level Virtual Machine project (LLVM) [7] was intended to take all state-of-the-art principles for building compilers into account. In contrast to the legacy structure of the compiler, comprising a front end, optimizer, and back end, LLVM was created from the outset to fulfill the aim of developers to compile and optimize more than one language. For this reason, they created an internal version of the program code after the compilation process that is known as LLVM Intermediate Representation (IR).

The now very popular Clang compiler [8], which compiles C, C++, Objective C, and Objective C++, also stems from this project. With the help of Clang, LLVM generates executable code, not only on known desktop systems from the x86 processor family, but also on ARM systems, as used by most mobile devices.

A Ruby interpreter decides at program run time the variable type based on its situation. Dave Thomas, the author of the first language reference for Ruby [9], called this Duck Typing. The term goes back to the poem "Little Orphant Annie" by US poet James Whitcomb Riley, which says: "See a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck." In Listing 2, Ruby determines the type of variable a at run time.

Listing 2

Ruby Type Determination

 

In contrast to Ruby, Crystal identifies the type of a at compilation time. It comes as little surprise then that Listing 2 causes totally different error messages in Ruby and Crystal. Ruby runs the first puts command (line 3) but refuses to add the string to an integer (Figure 3).

Figure 3: Ruby runs the first part of the script but refuses to add a string to an integer.

The Crystal compiler also stumbles across this problem, but – in contrast to Ruby – does not even run the program up to the line with the first puts (Figure 4). Crystal not only detects the right types for simple literals such as nil, false, 1, 3.14159265, 'a', :crystal, and "crystal" but also correctly typecasts assignments such as a = 1 and function arguments.

Figure 4: Listing 2 did not even compile in Crystal.

As an example, Ruby's language syntax lets you define a simple subtraction function as follows:

def sub(a,b)
  a - b
end

The application can use this function in different ways:

c = sub(2,1)
c = sub(2.5,1.6)

Crystal also supports this argument typecasting through alternative functions (for Int32 on the one hand, and for Float64 on the other).

However, Crystal can do more. Using the build command, it influences the production of code in multiple ways. For example, the call

$ crystal build --release bench1.cr

creates a size-optimized executable that can run without Crystal. The tool option, on the other hand, displays the source code of a Crystal file, so the developer can troubleshoot it. The command

$ crystal tool types bench1.cr

shows the different variable types that Crystal assigns to the program during the compilation process.

Language Functionality

Crystal solves some problems differently from Ruby. Unions are an important language construct that makes the code more flexible with static typecasting. A call to

alias Int32OrString = Int32 | String

generates the type Int32OrString, which can optionally be an integer or a string.

Another interesting case has Crystal using Procs, function pointers with optional contexts, to create anonymous functions of the following type:

a = ->(x : Int32, y : Int32) { x - y }
a.call(42, 23) #=> 19

If you want to manage multiple processes in Crystal programs, you can resort to fiber and channel constructs, which are known from the Go and Erlang languages. Listing 3 shows a socket server that creates 10 processes to serve a corresponding number of clients.

Listing 3

Channel, Spawn

 

Integrating C libraries also makes sense. For example, you can use the declaration in Listing 4 to include the mathematical functions of the standard C library. Using the structs known from C, you can also implement declarations like the one in Listing 5.

Listing 4

Math with Libc

 

Listing 5

Structs

 

Speed Benefits

To evaluate the speed benefits that Crystal seems to have, you need applications that can do more than just optimize loops. Table 1 shows a speed comparison between Ruby, JRuby, and Crystal for the multiplication of two 100x100 matrixes [10].

Table 1

Computing 100 x 100 Matrixes

Language

Time (s)

Crystal

0.005

Ruby

0.136

JRuby

3.874

jrubyc

5,207

To keep this comparison fair, the program simply measures the execution time; it does not include the time that Crystal requires for compilation. Two values were determined for JRuby: direct execution time and a Java class file, which was created with the JRuby compiler (jrubyc), so it could run on the Java Virtual Machine. The results for computing a Fibonacci sequence are similar (Table 2).

Table 2

Fibonacci Sequence

Language

Time (s)

Crystal

0.206

Ruby

0.250

JRuby

3.849

jrubyc

5,311

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Oracle Launches JRuby Website

    Oracle has just released the first, major JRuby application, thus demonstrating the viability of the Java port of the Ruby scripting language.

  • Free Software Projects

    The free high-end game, Yo Frankie, in which players steer a flying squirrel through a colorful 3D world, is almost finished. KI Research still faces major issues, but FreeHAL, a dialog program, gives users a behind-the-scenes look at the current state of affairs.

  • Tech Tools
  • DeskTOPia: xdesktopwaves

    If even the most imaginative desktop background is starting to bore you, it’s time for a change. xdesktop waves allows you to change your desktop into a water scape. You can add rain and storms or just drag your mouse to liven things up.

  • News

    In the news: TUXEDO OS; Native GPU Driver for Apple Silicon; Linux Kernel 6.0; System76 Skips Pop!_OS 22.10 to Focus on COSMIC Desktop; New Look for System76 Thelio; Ubuntu Software Store Rumored to Replace Gnome Software; and a New Arch-Based Linux Distribution.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News