Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Crystal lang

Crystal lang

A introductory presentation about Crystal Lang, presented first at TCS in Turin.
https://crystal-lang.org/
http://torinocodingsociety.it/

Edoardo Tenani

May 22, 2017
Tweet

More Decks by Edoardo Tenani

Other Decks in Programming

Transcript

  1. Why? - Ruby friendly syntax - Static type checking -

    Modern stdlib - Compiles for LLVM - Easy to distribute - Garbage collected (Boehm-Demers-Weiser conservative gc) - Self-hosted
  2. A new language implies... - New syntax - New docs

    - New standard library - New ecosystem
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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)
  10. 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
  11. 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
  12. 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
  13. 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)
  14. 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
  15. 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) ^~~~~
  16. Type inference Using explicit type annotation. class Person @name :

    String @age : Int32 def initialize(name) @name = name @age = 0 end end
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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)
  23. 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 ^~~~~~
  24. 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
  25. 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!
  26. Blocks Declare a dummy block argument, as a last argument

    to clarify a method accepts a block. def twice(&block) yield yield end
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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)
  32. Procs To invoke a Proc, you invoke the call method

    on it. proc = ->(x : Int32, y : Int32) { x + y } proc.call(1, 2) #=> 3
  33. 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
  34. 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
  35. Procs ...even for local variables. def counter x = 0

    ->{ x += 1; x } end proc = counter proc.call #=> 1 proc.call #=> 2
  36. 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
  37. 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
  38. Macros Macros can access constants. VALUES = [1, 2, 3]

    puts {{VALUES.join(", ")}} #=> 1, 2, 3
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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"
  44. 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
  45. 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
  46. 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
  47. 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!
  48. 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}"
  49. 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 }
  50. recursion.cr def fact(n : Int) : Int if n ==

    0 return 1 end return n * fact(n - 1) end puts fact 7
  51. def.cr def sa(name : String) : Int32 3 end def

    foo(x, y : Int32 = 1, z : Int64 = 2) x + y + z end
  52. 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
  53. 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
  54. Q&A