Luis Lavena
Work: area17.com
Twitter: @luislavena
GitHub: luislavena
Slide 3
Slide 3 text
What if Ruby was created today?
Slide 4
Slide 4 text
Overview of features
● LLVM-backend
● Ruby-like syntax
● Compiles to native code
● Global type inference
● Compile time error checking
● Go-inspired concurrency design*
Slide 5
Slide 5 text
LLVM-backend
● Used by many new languages (ie. Rust, Swift)
● Simplifies architecture porting and cross compilation
● Advanced branching and native code optimizations
● Stands on the shoulders of giants
Slide 6
Slide 6 text
Ruby-like syntax
● Reduces learning curve
● Inspired but not directly copied
● many ways to do things vs one way (no aliases)
● Compiled, no VM or bytecode to interpret
● No eval or crazy meta-programming
● Self-hosted (Crystal is written in Crystal)
Slide 7
Slide 7 text
Ruby-like syntax, on steroids
● Initializer shortcuts
● Method overloading
● Macros
● Generics
● 21st century Standard Library
Slide 8
Slide 8 text
Initializer shortcuts
# Ruby
class Greeter
def initialize(entity)
@entity = entity
end
end
# Crystal
class Greeter
def initialize(@entity)
end
end
Slide 9
Slide 9 text
Method overload
# Ruby
def guess(value)
case value
when Integer
puts "Integer!"
when String
puts "String!"
else
puts "No idea!"
end
end
# Ruby
guess 10
# => "Integer!"
guess "hello!"
# => "String!"
guess true
# => "No idea!"
Slide 10
Slide 10 text
Method overload
# Crystal
def guess(value : Int32)
puts "Integer!"
end
def guess(value : String)
puts "String!"
end
def guess(value)
puts "No idea!"
end
# Crystal
guess 10
# => "Integer!"
guess "hello!"
# => "String!"
guess true
# => "No idea!"
Slide 11
Slide 11 text
Macros
● Executed before type inference
● Transform AST at compilation time
● Used for minimal meta-programming and callbacks
○ inherited (Class)
○ included (Module)
○ method_missing
○ delegate
○ setter/getter/property
○ respond_to?
Slide 12
Slide 12 text
Macros (expanded)
class Person
getter name
end
class Person
def name
@name
end
end
Slide 13
Slide 13 text
Type inference
● Always know what is being used and where
● Compile time checks beats runtime errors ;-)
Slide 14
Slide 14 text
Types
● String is not the same as Char
○ “a” vs ‘a’
● Int32 and Int64
● Float32 and Float64
● Nil
● Boolean
● Array of types (Array(T))
Slide 15
Slide 15 text
Types
a = [1, 2, 3].map { |x| x + 3 }
pp a # => a = [4, 5, 6]
pp typeof(a) # => Array(Int32)
b = [1, "a", 'b']
pp typeof(b) # => Array(String | Int32 | Char)
# b << 12.5 # => Compile error (Float32)
Slide 16
Slide 16 text
Modern Standard Library
● No legacy libraries ;-)
● HTTP Client and Servers built-in
● OAuth2 and Websockets
● YAML, JSON, XML, Markdown parsers
● Growing ecosystem (shards)
Slide 17
Slide 17 text
HTTP Server
require "http/server"
server = HTTP::Server.new(9292) do |request|
HTTP::Response.ok "text/plain", "Hello world!"
end
puts "Listening on http://0.0.0.0:9292"
server.listen
Slide 18
Slide 18 text
HTTP Server
$ crystal build --release http_server.cr
$ ./http_server
Listening on http://0.0.0.0:9292
Slide 19
Slide 19 text
HTTP Server (benchmark: wrk)
$ ./wrk -c 10 -t 2 http://localhost:9292/
Running 10s test @ http://localhost:9292/
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 97.90us 212.99us 13.47ms 99.67%
Req/Sec 55.93k 4.88k 112.00k 90.55%
1118367 requests in 10.10s, 107.72MB read
Requests/sec: 110733.34
Transfer/sec: 10.67MB
Slide 20
Slide 20 text
HTTP Server (prefork)
require "http/server"
server = HTTP::Server.new(9292) do |request|
HTTP::Response.ok "text/plain", "Hello world!"
end
puts "Listening on http://0.0.0.0:9292"
server.listen_fork(3)
Slide 21
Slide 21 text
HTTP Server (benchmark: wrk)
$ ./wrk -c 10 -t 2 http://localhost:9292/
Running 10s test @ http://localhost:9292/
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 128.90us 525.80us 12.09ms 97.18%
Req/Sec 87.02k 17.70k 117.77k 63.50%
1731802 requests in 10.01s, 166.81MB read
Requests/sec: 173042.01
Transfer/sec: 16.67MB
Slide 22
Slide 22 text
HTTP Server (memory)
# fresh
$ pmap -x $(http_server PID)
Address Kbytes RSS Dirty
total kB 69772 4408 776
total kB 111224 5116 1316
total kB 70264 4956 1156
Start
wrk
GC
JSON Parsing (schema)
require "json"
class Person
JSON.mapping({
name: String,
age: Int32,
})
end
Slide 25
Slide 25 text
JSON Parsing
data = %([{"name":"Luis","age":36},{"name":"Ana","age":21}])
Array(Person).from_json(data).each do |person|
if person.age > 30
puts "Getting old, #{person.name}?"
end
end
Slide 26
Slide 26 text
JSON Parsing
class Person
# … mapping …
def initialize(@name, @age)
end
end
user = Person.new("Aaron", 34)
user.to_json # => {"name":"Aaron","age":34}
Web frameworks
● Amethyst
● Kemal
● Moonshine
● Carbon
● Chocolate
● Beryl*
https://github.com/veelenga/awesome-crystal
Slide 33
Slide 33 text
Simplified deployment
● Single executable
● Small footprint and size
● Fast compile times
● Easy Heroku deploy*
https://github.com/scaint/heroku-buildpack-crystal
Slide 34
Slide 34 text
Community
● Small, friendly
● Code of Conduct
● New language, fewer libraries
● 3rd party libraries starting to show
● Less drama ;-)
https://github.com/veelenga/awesome-crystal