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,225