Crystal Introduction

by "Blag" - Senior Developer Evangelist

Return to Geeky Thursday

What is Crystal?


Syntax similar to Ruby.


Statically type-checked without need for type of variables or method arguments.


Can call C code using bindings.


Compiles to efficient native code.

How to install Crystal?


On Linux run this on the terminal


curl https://dist.crystal-lang.org/apt/setup.sh | sudo bash


Then simply do sudo apt-get install crystal.


On a Mac do brew install crystal-lang.


On Windows, do nothing because is not supported...

Who uses Crystal?


Even though Crystal is an alpha mode...there are companies using it in production


  • Diploid - Human genome interpretation
  • NeuraLegion - Machine Learning powered advance protocal fuzzer
  • Neopoly GmbH - Online games and services
  • Appmonit - Analytics engine
  • Blitline - Online image processing
  • Manas - Unconventional software for unconventional needs

Starting out...


Crystal doesn't come with a REPL...but it comes with a Web Playground!


Simply type crystal play

Open your browser and go to http://localhost:8080

Package Management


In order to get and use external libraries in Crystal we need to create a shard file by doing

shards init

Then adding the library information like this...

				
dependencies:

  toml:

    github: manastech/crystal-toml

    branch: master
                

Basic Concepts


Printing on the screen is easy...

And only one line comments are allowed...

				
puts "This is Crystal!"


#This is a Crystal comment 
				

Arrays are easy...


				
numArray = [1, 2, 3]

puts numArray


#[1, 2, 3]



strArray = %w(this is Crystal)

puts strArray


#["this", "is", "Crystal"]
				

Multidimension Arrays are easy too...


				
numArray = [[1, 2],[ 3, 4]]

puts numArray[0][1]


#1



puts numArray[1][1]


#3

Some fun with Arrays...

				
numArray = [1, 2, 3, 4]

numArray.push(5)

puts numArray # [1, 2, 3, 4, 5]


numArray.pop

puts numArray # [1, 2, 3, 4]


a = [1, 2]

b = [3, 4]

a.concat(b)

puts a # [1, 2, 3, 4]


puts numArray.first # 1

puts numArray.last # 4
				
			
numArray = [1, 2 ,3, 4]

puts numArray.shuffle() # [2, 3, 1, 4]



numArray.map! {|x| x * x}

puts numArray # [1, 4, 9, 16]



numArray.reverse!

puts numArray # [16, 9, 4, 1]



numArray = [[1, 2], [3, 4]] # [1, 2, 3, 4]

puts numArray.flatten
				

We can iterate on Arrays and Hashes...For doesn't exit


				
lang = ["Spanish","English","German"]

lang.each { |x| puts x }


#Spanish

#English

#German
				
				
lang = {"Spanish" => "Peru", 
	
        "English" => "US", 
	    
        "German" => "Germany"}

lang.each_key{|x| puts "#{x} is spoken in #{lang[x]}"}


#Spanish is spoken in Peru

#English is spoken in US

#German is spoken in Germany
				

Functions


Functions always return the last value


				
def concat(a : String, b : String): String

  a + " " + b

end


message = concat("Hello", "Crystal")

puts message # Hello Crystal
				

Although sometimes return is needed...


Classes


In Crystal, everything is an object


  • It has a type

  • It can respond to some methods
				
class Person

  property age


  def initialize(name : String)

    @name = name

    @age = 0

  end



  def name

    @name

  end



  def age

    @age

  end
  		
				
  def become_older(by = 1)

    @age += by

  end

end


blag = Person.new "Blag"

blag.age = 40

puts blag.name # Blag

puts blag.age # 40

blag.become_older

puts blag.age # 41

blag.become_older 5

puts blag.age # 46
				

Fibonacci List


Finally...we're going to make our first application...


So grab your favorite text editor and get ready...


Name your file "fibonacci.cr"


				
def fib(num : Int32, a : Int32, b : Int32) : String

  result = ""

  if a > 0 && num > 1

    result = result + " " + (a + b).to_s + 
    
             fib(num - 1, a + b, a)

  elsif a == 0

    result = a.to_s + " " + b.to_s + " " + 
    
            (a + b).to_s + fib(num - 1, a + b, b) 

  end 

  return result

end


print "Enter a number: "

num = gets.as(String).strip.to_i

puts(fib(num, 0, 1))
				

Open the Terminal and go to your source code folder...

crystal "Name_of_File.cr"


When we run it we're going to see...


Making an LED Number App


This is one of my favorite codes of all time...


Name your file "LED_Numbers.cr"


				
leds = {0 => [" _  ","| | ","|_| "],

        1 => ["  ","| ","| "],

        2 => [" _  "," _| ","|_  "],

        3 => ["_  ","_| ","_| "],

        4 => ["    ","|_| ","  | "],

        5 => [" _  ","|_  "," _| "],

        6 => [" _  ","|_  ","|_| "],

        7 => ["_   "," |  "," |  "],

        8 => [" _  ","|_| ","|_| "],

        9 => [" _  ","|_| "," _| "]}
				
				
print "Enter a number: "

num = gets.as(String).strip


i = 0

loop do

  j = 0

  loop do

    print leds[num[j].to_i][i]

    break if j == num.size - 1

    j += 1

  end

  puts ""

  break if i == 2

  i += 1

end
				

When we run it we're going to see...


Random Names


This App will generate 100,000 random names using two 16 elements arrays


We will measure the runtime


Name your file "Random_Names.cr"


				
start = Time.now


names = ["Anne","Gigi","Blag","Juergen","Marek",

         "Ingo","Lars","Julia", "Danielle","Rocky",
         
         "Julien","Uwe","Myles","Mike","Steven",
         
         "Fanny"]


last_names = ["Hardy","Read","Tejada","Schmerder",

              "Kowalkiewicz","Sauerzapf","Karg",

              "Satsuta","Keene","Ongkowidjojo",

              "Vayssiere","Kylau","Fenlon",

              "Flynn","Taylor","Tan"]
              

              
full_names = [] of String
				
				
i = 0

loop do

  full_names.insert(0, names[Random.rand(16)] + " " + 
  
  last_names[Random.rand(16)])

  break if i == 99999

  i += 1

end


puts Time.now - start
			

When we run it we're going to see...

How this behaves in Python?

And what about Go?

Crystal was faster this time!

Decimal to Romans


This App will create a Roman Numeral based on a decimal number


This will include some nice commands...


Name your file "Decimal_to_Roman.cr"


				
Roman_Table = {1000=> "M", 900=> "CM", 500=> "D", 

               400=> "CD", 100=> "C", 90=> "XC", 

               50=> "L", 40=> "XL", 10=> "X", 

               9=> "IX", 5=> "V", 4=> "IV", 1=> "I"}
			
				
def roman_number(number : Int32): String

  result = ""

  while number > 0

    Roman_Table.each_key{|x| 

      if number >= x 

        result += Roman_Table[x] 

          number -= x

          break 

      end }

  end

  return result

end
			
				
print "Enter a number: "

num = gets.as(String).strip.to_i

puts roman_number(num)
				

When we run it we're going to see...


Count Letters


In this example we're going to read a file and count how many time a letter appears...


Call your file "count_letters.cr" (all in lowercase).


Create a file called "readme.txt" with the following text...


"This is a text file that we're going to read it using Crystal"


				
line = File.read("readme.txt").strip

counter = {} of String => Int32

line.each_char{|x| 
	
  if counter.has_key?(x.to_s)

    counter[x.to_s] += 1

  else

    counter = counter.merge({x.to_s => 1})

  end }

counter.each do |key, value|

  puts "#{key} => #{value}"

end
				

When we run it we're going to see...


Going beyond...


Crystal has an amazing number of libraries and projects for a language that it not even beta...


We're going to take a look at a couple of this libraries...


Kemal - Web Framework


Kemal is a lighting fast, super simple framework written in Crystal


It's very similar to Ruby's Sinatra


Although according to Kemal's website...it's way faster than Sinatra


We're going to do things the Crystal way...

On the terminal type


crystal init app fibonacci


This will generate the folders and structure of the app...

Now we need to install "kemal"


Open the generated shard.yml

				
name: fibonacci

version: 0.1.0

authors:

  -  <a.tejada.galindo@sap.com>

targets:
  
  fibonacci:
   
    main: src/fibonacci.cr


crystal: 0.23.1


license: MIT


dependencies:

  kemal:

    github: kemalcr/kemal
				

Then run this (making sure your inside the fibonacci's folder)...


shards install


This will install the dependencies making your app ready to go...


Now open fibonacci.cr and copy the following code...


				
require "kemal"



def fib(num : Int32, a : Int32, b : Int32) : String

result = ""

  if a > 0 && num > 1

    result = result + " " + (a + b).to_s + 
     
             fib(num - 1, a + b, a)

  elsif a == 0

    result = a.to_s + " " + b.to_s + " " + 
    
             (a + b).to_s + fib(num - 1, a + b, b) 

  end 

  return result

end
				
				
get "/" do

  render "views/index.ecr"

end



post "/getfibo" do |env|

  num = env.params.body["num"]

  result = fib(num.to_i, 0, 1)

  render "views/getfibo.ecr"

end



Kemal.run
				

Create the "views" folder inside the fibonnaci folder...not inside the "src" folder...


Create two files one called "index.ecr" and the other one "getfibo.ecr"


				
<!doctype html>

<html>

<head>

  <title>Fibonacci List</title>

</head>

<body>

  


  <form action="getfibo" method="post">

    <input name='num' placeholder='Enter a number' />

    <input type="submit" value="Send">

  </form>

</body>

</html>
				
				
<!doctype html>

<html>

<head>

  <title>Fibonacci List</title>

</head>

<body>

  <h3>The fibonacci sequence is: <%= result %></h3>

  <a href="/">Go back<a/>

</body>

</html>

				

To run we simply need to do


crystal run src/fibonacci.cr


And then go to http://localhost:3000 on the browser...


2D fun with CrSFML


CrSFML is a binding for SFML (Simple and Fast Multimedia Library)


It allows us to create 2D games in a fast and easy way...


The installation request that you built SFML from the source so here are the Instructions


Then we need to compile the VoidCSFML by doing a cmake . && make && sudo make install


Just make sure the version correspond to the installed SFML

Finally create a shard.yml and pass the following...

				
dependencies:

  crsfml:

    github: oprypin/crsfml

    version: 2.4.10
				

Make sure you run shards install


If there's a complaint about not founding a library...simply run


ldconfig


				
require "crsfml"


window = SF::RenderWindow.new(SF::VideoMode.new(538, 

         300), "Nyan Cat!")


left_right = 0

up_down = 50

clock = SF::Clock.new


while window.open?

  while event = window.poll_event

    if event.is_a? SF::Event::Closed

      window.close

    end

  end
				
				
  texture = SF::Texture.from_file("NyanCat.png")

  texture.smooth = false

  sprite = SF::Sprite.new(texture)

  sprite.position = SF.vector2(left_right, up_down)
  

  if SF::Keyboard.key_pressed?(SF::Keyboard::Escape)

    window.close();

  elsif SF::Keyboard.key_pressed?(SF::Keyboard::Left)

    if left_right > 0

      left_right -= 1  

    end

  elsif SF::Keyboard.key_pressed?(SF::Keyboard::Right)

    if left_right < 400

      left_right += 1

    end
				
				
  elsif SF::Keyboard.key_pressed?(SF::Keyboard::Up)

    if up_down > 0

      up_down -= 1  

    end

  elsif SF::Keyboard.key_pressed?(SF::Keyboard::Down)

    if up_down < 220

      up_down += 1

    end    	

  end  
				
				
  window.clear(SF::Color.new(64,64,64, 255))


  sprite.position = SF.vector2(left_right, up_down)


  window.draw(sprite)


  window.display


end
				

We can run this as a regular Crystal file...

We can move the cat up/down and left/right using the cursor keys

We can close the app by pressing "esc"

That's it for now


Crystal is an awesome and impressive language


Even thought is still in Alpha...


Anyway...there's a huge community and a ton of libraries and resources...


It wouldn't surprise me at all if more people start to favor Crystal over Ruby...

Contact Information


Blag --> a.tejada.galindo@sap.com

@Blag on Twitter

Go back home...