Slide 1

Slide 1 text

"Fast as C, slick as Ruby"

Slide 2

Slide 2 text

Edoardo Tenani @edoardotenani github.com/endorama Freelance, Full-stack web developer, small IoT projects

Slide 3

Slide 3 text

Our Challenge ( Crystal core team ) static types + compiled + "Ruby feeling"

Slide 4

Slide 4 text

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

Slide 11

Slide 11 text

Stdlib - Adler32 - ArgumentError - Array - Atomic - Base64 - Benchmark - BigFloat - BigInt - BigRational - BitArray - Bool - Box - Bytes - Channel - Char - Class - Colorize - Comparable - Complex - Concurrent - CRC32 - Crypto - Crystal - CSV - Debug - Deque - Digest - Dir - DivisionByZero - DL - ECR - Enum - Enumerable - ENV - Errno - Exception - Fiber - File - FileUtils - Flate - Float - Float32 - Float64 - GC - Gzip - Hash - HTML - HTTP - Indexable - IndexError - INI - Int - Int16 - Int32 - Int64 - Int8 - InvalidByteSe quenceError - IO - IPSocket - Iterable - Iterator - JSON - KeyError - Levenshtein - LLVM - Logger - Markdown source

Slide 12

Slide 12 text

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

Slide 38

Slide 38 text

Blocks yield can receive arguments. def twice yield 1 yield 2 end twice do |i| puts "Got #{i}" end twice { |i| puts "Got #{i}" } # => Got 1 # => Got 2 # => Got 1 # => Got 2

Slide 39

Slide 39 text

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

Slide 49

Slide 49 text

Macros Macros can access constants. VALUES = [1, 2, 3] puts {{VALUES.join(", ")}} #=> 1, 2, 3

Slide 50

Slide 50 text

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}"

Slide 62

Slide 62 text

enumerable-functions.cr strs = ["peach", "apple", "pear", "plum"] puts strs.index("pear") puts strs.includes?("grape") puts strs.any? { |v| v.starts_with? "p" } puts strs.all? { |v| v.starts_with? "p" } puts strs.select { |v| v.includes? "e" } puts strs.map { |v| v.upcase }

Slide 63

Slide 63 text

time-formatting-parsing.cr puts Time.parse("2012-11-01 22:08:12", "%F %T") puts Time.parse("Fri Oct 31 23:00:24 2014", "%c") puts Time.parse("20150624", "%Y%m%d")

Slide 64

Slide 64 text

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

Slide 72

Slide 72 text

Happy Crystalling!

Slide 73

Slide 73 text

Q&A

Slide 74

Slide 74 text

Resources https://crystal-lang.org/ https://github.com/crystal-lang/crystal/ https://github.com/veelenga/awesome-crystal http://www.crystalforrubyists.com https://crystal-lang.org/docs/overview/ https://crystal-lang.org/api/0.21.1/ https://play.crystal-lang.org/#/cr https://github.com/tbpgr/crystal_samples https://github.com/askn/crystal-by-example