Vectormike's Blog

What to expect from Crystal 1.0

Crystal is a Ruby-like programming language with the power of C. Ruby/Rails developers especially should try this language because of its similarity to Ruby in syntax and pure elegance. Crystal offers the performance and efficiency of C, which is mostly used to write low-level systems with ease.

In this article, we will discuss the following topics, along with a few basic things Crystal can offer:

What is Crystal? Crystal was originally named Joy and began development in 2011. The first official version was released in 2014 and since then has been under constant development until March 2021, when Crystal 1.0 arrived.

Crystal is a statically typed systems programming language with several important developer-friendly goals. Inspired by Ruby, Crystal has a gentle learning curve that makes it easy to read and write. It also offers type checking at runtime but does not always require defining variable types or method arguments.

The team behind Crystal has made it clear that its similarity to Ruby is not the language’s raison d’être. While inspired by Ruby’s productivity, Crystal compiles to native code with the use of LLVM and uses type inference, which automatically concludes the type of an expression at compile time.

Concurrency in Crystal works similarly to Go in that it uses threads called “fibers” to communicate without having to share memory. This is different from parallelism; two code paths cannot be executed at the same time, but are dependent on the previous task’s completion before the next task can begin. Crystal fibers are similar to OS threads except that they are lightweight and are managed by the process internally.

Crystal also boasts the ability to call C code just by writing bindings to it, which gives us the ability to pass data between the two. This allows us to take advantage of the strengths of both languages.

The team also clarified that Crystal will be backwards-compatible with previous versions, meaning that while the built-in libraries are being updated, they will still work without an update. These built-in libraries are grouped into “shards” and dispensed using Git. A Shard is a package of Crystal code with built-in commands from its respective repository, available when you specify them through a YAML file.

Before Crystal 1.0, there had been prior releases that lacked the significant language and compile stability of the current version, which have made Crystal particularly useful. Future releases will include bug fixes and other maintenance and will be regarded as patch releases.

Getting started with Crystal

Crystal supports many different platforms, ranging from macOS to WindowsSubsystem for Linux. Crystal compiler is yet to run on Windows, but it is on the roadmap for future releases.

If you are using macOS, you can use Homebrew, which will help you install the latest version:

brew update
brew install crystal

For WindowsSubsystem for Linux (Ubuntu and Debian), you can write the following:

curl -fsSL https://crystal-lang.org/install.sh | sudo bash

Contributing to Crystal means that you will have to install it from sources.

Writing our first Crystal app and HTTP server

Our first program will be “Hello World” because it is a large, complex program (just kidding)! This is what a simple “Hello World” looks like in Crystal, and we will save it in a hello_world.cr file:

puts "Hello World!"

puts in the code above stands for “put string.” The entire program is mainly a call to the method puts, which then outputs the "Hello World!".

We then run it like this:

$ crystal hello_world.cr
Hello World!

The HTTP server code sample looks like this:

require "http/server"

server = HTTP::Server.new do |context|
  context.response.content_type = "text/plain"
  context.response.print "Hello world! This is our server"
end

address = server.bind_tcp 3000
puts "Listening on http://#{address}"
server.listen

This makes more sense if you become familiar with the language, but a few things we can take are:

require "http/server"

We use require to require files, which hold the code we need:

address = server.bind_tcp 3000

The port is set using the method bind_tcp. This method is from the object HTTP::Server.

Assigning variables

Assigning variables works like this:

name = "Victor!"
age = 23

p! name
p! age

p! is similar to puts, only that p! prints the expression in code:

age         # => 23

Reassigning a variable is a requirement:

age = 23
p! age
age = 24
p! age

Control expressions

Similar to how control expressions work in other languages, Crystal uses the following:

name = "Victor!"
age = 23
if age > 23
  name = "Matt"
elsif age < 23
  name = "Mike"
else
  name
end

Logical and and or operators work with Crystal, too. The and is if — and only if — the operands are true. The sample below is the same as the one in the documentation:

some_exp1 && some_exp2

# The above is the same as:
tmp = some_exp1
if tmp
  some_exp2
else
  tmp
end

Logical or or || are syntactical sugar for if:

some_exp1 || some_exp2

# The above is the same as:
tmp = some_exp1
if tmp
  tmp
else
  some_exp2
end

Defining methods

To define a method in Crystal, we use the keyword def, followed by the method name. After the expression comes the end keyword:

def greet
  puts "Hello Victor! It is nice to have you here"
end

greet()

Several methods can have the same definition but with different parameters. This is called method overloading, which improves the readability of the program:

def say_hello(arg : String)
  puts "You are #{arg}!"
end


def say_hello(age : Int32)
  puts "You are age" * age
end

say_hello "Victor"
say_hello 23

Type reflections

Crystal also supports type reflections by providing methods such as typeof, as, as?, responds_to, nil, and is_a?.

Error handling in Crystal

Error handling in Crystal is done by raising and rescuing exceptions. This means that for every error encountered, you will need to raise an exception and handle it (or rescue, in Crystal) by specifying a variable in the rescue clause or by simply outputting a message. It is much easier to understand if you read the code below:

begin
raise "An Error!"
rescue
puts "We caught it for you boss!"
end

# Output: Rescued!

While specifying a variable in the rescue clause, you can now access the exception:

begin
  raise "An Error!"
rescue ex
  puts ex.message
end

# Output: An Error!

raise is a method with overloading, and it accepts String and Exception instances:

raise "An Error!"
raise Exception.new("An Error!")

The future of Crystal With contributions from more than 450 developers for v1.0, it is easy to see that Crystal is going to be well-loved in the future. The current release is all about language stability, which has set a high standard and made a huge step forward for the language.

There are currently 164 sponsors contributing to the sustenance of Crystal’s development. There have been 82 commits since version 0.36.1 made by 25 contributors; you can view the release changelog here.

The Crystal team still needs to make several adjustments, and I am hoping to see them in the subsequent maintenance releases. A roadmap has also been laid out for what still has to be done here.

Crystal’s documentation will also provide a better understanding of how things work, and be sure to check out their blog, where the team shares announcements and release notes.