What's Crystal?
@maiha
2016.11.29
Overview
Specifications
Practical cases
C
Fast
Difficult (pointers)
Memory leaks
Ruby
Easy (Readability, GC)
Slow
char *path = NULL;
if (config == NULL) {
fr = zmalloc(sizeof(*fr));
run_callback(void (*cb)
...require "redis"
redis = Redis.new
redis.get("foo")xxx BC
We want both!
Trade off
(cpu 0.000 total)
undefined method ...
from test.rb:8:in `<main>'

but,Runtime Error
Engineers!
AD 199x
Fast!
TYPE
and
Compile
AD 1999
Scala
Type and Compile!
JVM(jar)
JVM(null, type erasure)
Type(strictly)
No more null
No more JVM
No more work around
var user = new User
user = null // PASS
if (user != null) {
...
// jar returns nullOther languages
COOL!
We want both!
AD 200x

user = User.new
user = null // Compile error
Array(String).new // Keep type
but,No implements
Strictly Typed!
Academics!

on LLVM


(manas.tech)
AD 2012
GOD comes!
Really GOD!
Crystal is
- C
- Ruby
- (You say)
- Scala
- Typed
- (Ahhh)
- LLVM

Fast
null and pitfall free
def parse(email : String)
email.split("@",2)
end
parse(nil)
no overload matches 'parse'
with type Nil
Overloads are:
- parse(email : String)Easy
Type safety (compile-time)
Stable, CLang-friendly
Compile Error
Overview
Specifications
Practical cases
Crystal
install
% crenv local 0.20.0package manager (like rubygems)
% vi shard.yml
% shards updatetest unit library (Spec)
% crystal speccompiler (native code via LLVM)
% crystal build foo.crweb framework (like Sinatra)
KemalBoehm GC
bundled
bundled
foo.should eq(XXX)Ruby
Crystal
require "redis"
redis = Redis.new
redis.get("foo")Syntax
require "redis"
redis = Redis.new
redis.get("foo")def parse(email)
email.split("@", 2)
end
parse("a@b")
# => Arraydef parse(email)
email.split("@", 2)
end
parse("a@b")
# => Array(String)Ruby
Crystal
a = []
a = Array.new
h = {}
h = Hash.newType (Generic)
a = [] of String
a = Array(String).new
h = {} of String => Int32
h = Hash(String, Int32).newarray = [1, "a"]class Array(T)
include Enumerable(T)
class Hash(K, V)
include Enumerable({K, V})Ruby
Crystal
Tuple
t = [1, "a"]
# Array
t[0] # => 1 (Fixnum)
t[1] # => "a" (String)
t[2] # => nilt = {1, "a"}
# Tuple(Int32, String)
t[0] # => 1 (Int32)
t[1] # => "a" (String)
t[2] # index out of boundsCompile Error
X = Struct.new(:i, :s)
X.new(1, "a")
X.new(1, 2)record X,
i : Int32,
s : String
X.new(1, "a")
X.new(1, 2)Compile Error
Type Inference
def foo(a)
def foo(a) : Int32
def foo(a : String)
def foo(a : String) : Int32not supported yet
i : Int32 = 0class User
@name : String = "(no name)"
class User
def initialize(name : String)
@name = nameauto
method(args,ret)
local vars
required
instance vars
Type Inference(2)
class User
def name
@name || "?"
end
end
User.new.name
instance vars
must be initialized
Union Type(1)
def foo(a : Int32 | String)
def foo(a : Int32 | String | Array(Int32 | Int64))class Cache
def get(key)
if found
return value
else
return nil
# => (String | Nil)class Cache
def get(key) : String | Nil
class Cache
alias Result = String | Nil
def get(key) : Result
class Cache
def get?(key) : String?Nilable
Union Type(2)
class Parser
def parse(str)
@parsed ||= "x" # [STUB]
@parsed.upcase
# undefined method 'upcase' for Nil
# (compile-time type is (String | Nil))
not_nil! (Foo? → Foo)
@parsed ||= "x" # [STUB]
@parsed # String | Nil
@parsed.not_nil! # String
@parsed.not_nil!.upcaseBlock
Ruby
Crystal
[1,2].map(&:to_s)[1,2].map(&.to_s)
[1,2].map(&.to_s.size)def foo(&block)def foo(&block)
block with types
def foo(&block : Int32 ->)
def foo(&block : -> String)
def foo(&block : Int32 -> String)Useful features
class User
def initialize(@name : String)
endset instance variables in args
useful methods
bundled
Object#delegateObject#trymodule scope
class Foo::Bar
X # => ::X (ruby)
# => Foo::X (crystal)
endnice!
Macro
foo = 1
p foo1Ruby
macro p(v)
puts "{{v.id}}: #{{{v}}.inspect}"
endfoo: 1Crystal
1Crystal + macro p
→
v → Var("foo") < ASTNode
{{v.id}} →"foo"
{{v}} → foo
#{foo.inspect}
foo = 1
p foo→
pp bundled
CLang friendly
@[Link("foo")]
lib LibFoo
fun xxx(x0 : UInt32) : UInt32
end
p LibFoo.xxx(0_u32)Scala
JVM
→
jar
LLVM
Crystal
c-lib
→
→
cc ... -lgc -lfooauto link
direct access!
CLang (auto gen)
@[Include("rocksdb/c.h", prefix: %w(rocksdb_))]
@[Link("rocksdb")]
lib LibRocksDB
endcrystal_lib
→
→
schema
code
crystal src/main.cr -- schema-filelib LibRocksDB
fun open = rocksdb_open(options : Rocksdb...
type RocksdbOptionsT = Void*Spec(1)
require "spec"
describe "Foo" do
it "bar" do
baz.should eq(XXX)
baz.should_not eq(XXX)
expect_raises(Foo::NotFound, "xxx") do
baz.get("x")
end
spec2
describe "Foo" do
subject { Foo.new }
let(key) { "x" }Spec(2)
describe "Foo" do
def setup
end
it "bar" do
setupspec2
describe "Foo" do
describe "(case 1)" do
it "" doprivate def setup
end
describe "Foo" do
it "bar" do
setupcompile error
→
compile error
Overview
Specifications
Practical cases
Speed
good!
Benchmarks
optimized compile
% crystal build --release foo.crStability
good!
no runtime errors! no memory leaks!
Small service (prod): 6 months
Once compiled, we won!
| python | crystal | redis | crystal | grafana |
|---|---|---|---|---|
| dstat | dstat-redis | (cluster) | grafana-redis | (http) |
10 servers, 300bytes(JSON),1qps
Large service
not yet
CLI apps (prod): 18 months
excellent!
kafka-ping, redis-cluster-managing, stress-testing
Stability (FYI)
be careful
printf, format
libopenssl.a (ubuntu-16.04)
libxml2.a (ubuntu-16.04)
name = "foo"
printf("%i", name)invalid Int32: foo (ArgumentError)compile ok, but
Int32 overflow (not upgrade to Int64)
2147483647 → -2147483648native library itself
BUG
Deploy
Static linked native code!
Just scp!
ubuntu, CentOS, SL, ...
java versions, ruby version, maven is down, proxy,...
no chefs, no ansibles, ...
excellent!
compile
binary
host
→
→
What's Crystal?
By maiha
What's Crystal?
- 2,591