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 null
Other 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.0
package manager (like rubygems)
% vi shard.yml
% shards update
test unit library (Spec)
% crystal spec
compiler (native code via LLVM)
% crystal build foo.cr
web framework (like Sinatra)
Kemal
Boehm 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")
# => Array
def parse(email)
email.split("@", 2)
end
parse("a@b")
# => Array(String)
Ruby
Crystal
a = []
a = Array.new
h = {}
h = Hash.new
Type (Generic)
a = [] of String
a = Array(String).new
h = {} of String => Int32
h = Hash(String, Int32).new
array = [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] # => nil
t = {1, "a"}
# Tuple(Int32, String)
t[0] # => 1 (Int32)
t[1] # => "a" (String)
t[2] # index out of bounds
Compile 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) : Int32
not supported yet
i : Int32 = 0
class User
@name : String = "(no name)"
class User
def initialize(name : String)
@name = name
auto
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!.upcase
Block
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)
end
set instance variables in args
useful methods
bundled
Object#delegate
Object#try
module scope
class Foo::Bar
X # => ::X (ruby)
# => Foo::X (crystal)
end
nice!
Macro
foo = 1
p foo
1
Ruby
macro p(v)
puts "{{v.id}}: #{{{v}}.inspect}"
end
foo: 1
Crystal
1
Crystal + 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 -lfoo
auto link
direct access!
CLang (auto gen)
@[Include("rocksdb/c.h", prefix: %w(rocksdb_))]
@[Link("rocksdb")]
lib LibRocksDB
end
crystal_lib
→
→
schema
code
crystal src/main.cr -- schema-file
lib 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
setup
spec2
describe "Foo" do
describe "(case 1)" do
it "" do
private def setup
end
describe "Foo" do
it "bar" do
setup
compile error
→
compile error
Overview
Specifications
Practical cases
Speed
good!
Benchmarks
optimized compile
% crystal build --release foo.cr
Stability
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 → -2147483648
native 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,208