Why?
- Ruby friendly syntax
- Static type checking
- Modern stdlib
- Compiles for LLVM
- Easy to distribute
- Garbage collected (Boehm-Demers-Weiser conservative gc)
- Self-hosted
Slide 5
Slide 5 text
Self-hosted → Write Crystal to write Crystal
Slide 6
Slide 6 text
Another one…?
Slide 7
Slide 7 text
A new language implies...
- New syntax
- New docs
- New standard library
- New ecosystem
Slide 8
Slide 8 text
New syntax
Crystal’s syntax
is heavily
inspired on
Ruby’s.
# A very basic HTTP server
require "http/server"
server = HTTP::Server.new(8080) do |context|
context.response.content_type =
"text/plain"
context.response.print "Hello world,
got #{context.request.path}!"
end
puts "Listening on http://127.0.0.1:8080"
server.listen
Slide 9
Slide 9 text
New docs
Emh....
@asterite: "@yukihiro_matz Is it OK if we
copy some of Ruby's documentation (classes
and methods) in Crystal?"
@yukihiro_matz: "@asterite yes. I copied from
Perl in its early stage."
source
Slide 10
Slide 10 text
New stdlib
New means modern
- Spec ( Rspec like )
- CSV, JSON, XML, YAML, Markdown Parsers
- HTTP Client & Server + WebSockets
- Oauth & Oauth2 implementations
- SecureRandom, Crypto
- Gzip, HTML, OpenSSL
- ...
source
Stdlib
- Math
- Mutex
- NamedTuple
- Nil
- Number
- OAuth
- OAuth2
- Object
- OpenSSL
- OptionParser
- PartialComparable
- Pointer
- PrettyPrint
- Proc
- Process
- Random
- Range
- Readline
- Reference
- Reflect
- Regex
- SecureRandom
- Set
- Signal
- Slice
- Socket
- Spec
- StaticArray
- String
- StringPool
- StringScanner
- Struct
- Symbol
- System
- TCPServer
- TCPSocket
- Tempfile
- Termios
- Time
- Tuple
- TypeCastError
- UDPSocket
- UInt16
- UInt32
- UInt64
- UInt8
- Unicode
- Union
- UNIXServer
- UNIXSocket
- URI
- Value
- XML
- YAML
- Zip
- Zlib
Source
Slide 13
Slide 13 text
New ecosystem
YEAH!
- Crystalshards - ~1650 shards
- Awesome Crystal list - 400+ links
source
Slide 14
Slide 14 text
Notable shards
- kemal: Fast, Effective, Simple web framework
- sidekiq.cr: Simple, efficient job processing
- amethyst: a Rails inspired web-framework
- crystal-db: common db api for crystal
- crystal-pg: a postgres driver for crystal
- active_record.cr: Active Record pattern implementation for Crystal.
- immutable: Thread-safe, persistent, immutable collections
- commander: Command-line interface builder
- crystalline: A collection of containers & algorithms
- micrate: database migration tool
- NuummiteOS: OS Kernel written in Crystal (POC)
- crystal-gl: OpenGL bindings
- tons more!
source
Slide 15
Slide 15 text
It rocks!!!
Slide 16
Slide 16 text
But how fast is it?
Slide 17
Slide 17 text
App Server Requests/sec Latency in ms
(avg/stdev/max)
Memory
(MB)
CPU
(%)
Threads
nbr.
Plug with Cowboy 42601.09 12.38/22.13/228.85 51.56 415.9 22
Rack with Puma 52033.37 0.26/0.50/6.26 ~230 ~420 80
Node Cluster 76621.85 1.48/1.66/57.26 ~316 ~551 48
Rust Hyper 83196.50 1.20/0.22/4.18 27.71 350.4 9
Gunicorn
with Meinheld
83268.50 1.22/0.17/11.70 ~72 ~349 9
GO ServeMux 85345.17 1.09/0.17/5.23 9.06 410.1 17
Crystal HTTP 115570.61 0.86/0.10/6.92 8.99 112.7 8
source Platform: MacBook PRO 15 mid 2015, 2,2 GHz Intel Core i7 (4 cores)
Slide 18
Slide 18 text
Base64
Language Time (s) Memory (MiB)
C 1.85 32.2
Crystal 2.30 113.8
Rust 2.38 40.8
Ruby 2.77 130.4
Javascript Node 4.76 551.5
Php 6.34 53.4
Python 7.62 52.6
Go 8.40 95.7
Slide 19
Slide 19 text
JSON
Language Time (s) Memory (MiB)
Rust Pull 0.52 207.7
C++ Rapid SAX 0.72 1.0
Crystal Schema 2.05 337.2
Javascript Node 3.21 863.7
Python ujson 5.07 1352.9
Go 5.30 479.3
Php 6.37 1502.0
Ruby 8.67 1074.6
Slide 20
Slide 20 text
But how cool is it?
Slide 21
Slide 21 text
Everything is an Object
- has a type
- can respond to some methods
Slide 22
Slide 22 text
Type system
- Statically type check
- Built-in type inference
def shout(x)
# Notice that both Int32 and
String respond_to `to_s`
x.to_s.upcase
end
foo = ENV["FOO"]? || 10
typeof(foo) # => (Int32 | String)
typeof(shout(foo)) # => String
Slide 23
Slide 23 text
Union types
The type of a variable or
expression can consist of
multiple types.
if 1 + 2 == 3
a = 1
else
a = "hello"
end
typeof(a) # => (Int32 | String)
Slide 24
Slide 24 text
Type restrictions
Type restrictions are type
annotations put to method
arguments to restrict the
types accepted by that
method.
def add(x : Number, y : Number)
x + y
end
# Ok
add 1, 2
# Error: no overload matches
'add' with types Bool, Bool
add true, false
Slide 25
Slide 25 text
Type inference
If you omit an explicit type
annotation the compiler will
try to infer the type of
instance and class variables
using a bunch of syntactic
rules.
class Person
def initialize(name)
@name = name
@age = 0
end
end
# @age is inferred to be a Int32
Can't infer the type of instance
variable '@name' of Person
def initialize(@name)
^~~~~
Slide 26
Slide 26 text
Type inference
Using explicit type
annotation.
class Person
@name : String
@age : Int32
def initialize(name)
@name = name
@age = 0
end
end
Slide 27
Slide 27 text
Type inference
Assigning a variable that is
a method argument with a
type restriction.
class Person
def initialize(name : String)
@name = name
@age = 0
end
end
Slide 28
Slide 28 text
Modules
Modules serve two purposes:
- as namespaces for
defining other types,
methods and constants
- as partial types that
can be mixed in other
types
module Curses
class Window
end
end
Curses::Window.new
Slide 29
Slide 29 text
Modules
Modules serve two purposes:
- as namespaces for
defining other types,
methods and constants
- as partial types that
can be mixed in other
types
class Items
include ItemsSize
extend Size
end
Slide 30
Slide 30 text
Modules
An include makes defined
module methods instance
methods of the type.
module ItemsSize
def size
items.size
end
end
class Items
include ItemsSize
def items
[1, 2, 3]
end
end
items = Items.new
items.size #=> 3
Slide 31
Slide 31 text
Modules
An extend makes defined
module methods class
methods of the type.
module SomeSize
def size
3
end
end
class Items
extend SomeSize
end
Items.size #=> 3
Slide 32
Slide 32 text
Generics
Parameterize a type based on
other type.
class MyBox(T)
def initialize(@value : T)
end
def value
@value
end
end
puts MyBox.new(1).value
# MyBox(Int32).new(1).value
# => 1 (Int32)
puts MyBox.new("hello").value
# MyBox(String).new("hello").value
# => "hello" (String)
Slide 33
Slide 33 text
NULL reference
checks
All types are non-nilable in
Crystal, and nilable
variables are represented as
an union between the type
and nil.
if rand(2) > 1
my_string = "hello world"
end
puts my_string.upcase
Error in hello_world.cr:5:
undefined method 'upcase'
for Nil (compile-time type is
(String | Nil))
puts my_string.upcase
^~~~~~
Slide 34
Slide 34 text
Method
Overloading
Different methods, same name
and different number or type
of arguments.
class Person
@age = 0
def become_older
@age += 1
end
def become_older(years)
@age += years
end
end
john = Person.new "John" # age = 0
John.become_older # age = 1
john.become_older 5 # age = 6
Slide 35
Slide 35 text
Blocks
Methods can accept a block
of code that is executed
with the yield keyword.
def twice
yield
yield
end
twice do
puts "Hello!"
end
twice { puts "Hello!" }
# => Hello!
# => Hello!
# => Hello!
# => Hello!
Slide 36
Slide 36 text
Blocks
Declare a dummy block
argument, as a last argument
to clarify a method accepts
a block.
def twice(&block)
yield
yield
end
Slide 37
Slide 37 text
Blocks
Two methods, one that yields
and another that doesn't,
are considered different
overloads.
class Person
@age = 0
def become_older
@age += 1
end
def become_older(years)
@age += years
end
def become_older(&block)
@age += yield @age
end
end
Blocks
yield can receive multiple
arguments.
Each block variable has the
type of every yield
expression in that position.
def some
yield 1, 'a'
yield true, "hello"
yield 2, nil
end
some do |first, second|
# first is ( Int32 | Bool )
# second is
( Char | String | Nil )
end
Slide 40
Slide 40 text
Blocks
The yield expression itself
has a value: the last
expression of the block.
def twice
v1 = yield 1
puts v1
v2 = yield 2
puts v2
end
twice do |i|
i + 1
end
# => 2
# => 3
Slide 41
Slide 41 text
Procs
A Proc represents a function
pointer with an optional
context (the closure data).
# A proc without arguments
->{ 1 }
# Proc(Int32)
# A proc with one argument
->(x : Int32) { x.to_s }
# Proc(Int32, String)
# A proc with two arguments:
->(x : Int32, y : Int32) { x + y }
# Proc(Int32, Int32, Int32)
Slide 42
Slide 42 text
Procs
To invoke a Proc, you invoke
the call method on it.
proc = ->(x : Int32, y : Int32)
{ x + y }
proc.call(1, 2) #=> 3
Slide 43
Slide 43 text
Procs
A block can be captured and
turned into a Proc.
def int_to_int(&block :
Int32 -> Int32)
block
end
proc = int_to_int { |x| x + 1 }
proc.call(1) #=> 2
Slide 44
Slide 44 text
Procs
Captured blocks and proc
literals closure local
variables and self...
x = 0
proc = ->{ x += 1; x }
proc.call #=> 1
proc.call #=> 2
x #=> 2
Slide 45
Slide 45 text
Procs
...even for local variables.
def counter
x = 0
->{ x += 1; x }
end
proc = counter
proc.call #=> 1
proc.call #=> 2
Slide 46
Slide 46 text
No content
Slide 47
Slide 47 text
Macros
Macros are methods that
receive AST nodes at
compile-time and produce
code that is pasted into a
program.
macro define_method(name, body)
def {{name}}
{{body}}
end
end
# This generates:
#
# def foo
# 1
# end
define_method foo, 1
foo #=> 1
Slide 48
Slide 48 text
Macros
You can invoke a fixed
subset of methods on AST
nodes at compile-time. These
methods are documented in a
fictitious Crystal::Macros
module.
macro define_method(name, body)
def {{name.id}}
{{body}}
end
end
# This correctly generates:
# def foo
# 1
# end
# instead of
# def :foo
# 1
# end
define_method :foo, 1
Macros
You can iterate inside
macros.
macro define_dummy_methods(names)
{% for name, index in names %}
def {{name.id}}
{{index}}
end
{% end %}
end
define_dummy_methods
["foo", "bar", "baz"]
foo #=> 0
bar #=> 1
baz #=> 2
Slide 51
Slide 51 text
Macros
You can iterate outside
macros.
{% for name, index in
["foo", "bar", "baz"] %}
def {{name.id}}
{{index}}
end
{% end %}
foo #=> 0
bar #=> 1
baz #=> 2
Slide 52
Slide 52 text
Macros
You can use conditionals
inside macros.
macro define_method(name, content)
def {{name}}
{% if content == 1 %}
"one"
{% else %}
{{content}}
{% end %}
end
end
define_method foo, 1
define_method bar, 2
foo #=> one
bar #=> 2
Slide 53
Slide 53 text
Macros
You can use conditionals
outside macros.
{% if env("TEST") %}
puts "We are in test mode"
{% end %}
Slide 54
Slide 54 text
Macros Hooks
Special macros exist that are invoked as hooks, at
compile-time:
- inherited is invoked when a subclass is defined
- included is invoked when a module is included
- extended is invoked when a module is extended
- method_missing is invoked when a method is not found
Slide 55
Slide 55 text
Macros Hooks
Example of inherited macro
hook.
class Parent
macro inherited
def lineage
"{{@type.name.id}} < Parent"
end
end
end
class Child < Parent
end
Child.new.lineage
#=> "Child < Parent"
Slide 56
Slide 56 text
Concurrency
Crystal uses fibers, to
achieve concurrency. Fibers
communicate with each other
using channels without
having to turn to shared
memory or locks.
channel = Channel(Int32).new
total_lines = 0
files = Dir.glob("*.txt")
files.each do |f|
spawn do
lines = File.read(f)
.lines.size
channel.send lines
end
end
files.size.times do
total_lines += channel.receive
end
puts total_lines
Slide 57
Slide 57 text
C-Bindings
Crystal has a dedicated
syntax to easily call native
libraries, eliminating the
need to reimplement
low-level tasks
# Fragment of the BigInt implementation that
uses GMP
@[Link("gmp")]
lib LibGMP
alias Int = LibC::Int
alias ULong = LibC::ULong
struct MPZ
_mp_alloc : Int32
_mp_size : Int32
_mp_d : ULong*
end
fun init_set_str = __gmpz_init_set_str(
rop : MPZ*,
str : UInt8*,
base : Int) : Int
fun cmp = __gmpz_cmp(
op1 : MPZ*,
op2 : MPZ*) : Int
end
Slide 58
Slide 58 text
Dependencies
Crystal libraries are packed
as Shards, and distributed
via git
# shard.yml
name: my-project
version: 0.1
license: MIT
crystal: 0.22.0
dependencies:
mysql:
github: crystal-lang/crystal-mysql
version: ~> 0.3.1
Slide 59
Slide 59 text
Show me the code!
Slide 60
Slide 60 text
greeter.cr
class Greeter
def initialize(name : String)
@name = name
end
def salute
puts "Hello #{@name}!"
end
end
g = new Greeter("World")
g.salute # => Hello World!
Slide 61
Slide 61 text
argv.cr
# Demo of command line arguments
# ARGV[0]: First command line argument
# (not the executable name)
# ARGV is an array of strings
puts "Number of command line arguments: #{ARGV.size}"
ARGV.each_with_index { |arg, i| puts "Argument #{i}: #{arg}" }
# The executable name is available as PROGRAM_NAME
puts "Executable name: #{PROGRAM_NAME}"
recursion.cr
def fact(n : Int) : Int
if n == 0
return 1
end
return n * fact(n - 1)
end
puts fact 7
Slide 65
Slide 65 text
def.cr
def sa(name : String) : Int32
3
end
def foo(x, y : Int32 = 1, z : Int64 = 2)
x + y + z
end
Slide 66
Slide 66 text
macro.cr
class Object
def has_instance_var?(name) : Bool
{{ @type.instance_vars.map
&.name.stringify }}
.includes? name
end
end
person = Person.new "John", 30
person.has_instance_var?("name")
#=> true
person.has_instance_var?("birthday")
#=> false
Slide 67
Slide 67 text
Fikri: CLI todo app
Slide 68
Slide 68 text
Rubyist?
Check "Crystal for Rubyists Wiki page" and
"Crystal for Rubyist book"
Slide 69
Slide 69 text
WHOA! Deploy!
Slide 70
Slide 70 text
0.22
Current Crystal version.
State of Crystal, version 0.21, published on 2017/02/24
Slide 71
Slide 71 text
Roadmap → Crystal 1.0 by the end of 2017
"The major issue is clear: stability. While Crystal is a beautiful
language to play with, investing on using it at work to implement a
system that should be maintained for the long run seems risky for many
developers. And with good reason: we are still labeling Crystal as alpha
stage, even if it has been production-ready for quite some time already.
As such, and in line with our goal of seeing the language grow, we are
setting a new year resolution to have Crystal reach the 1.0 milestone in
2017."
Roadmap