# Examples taken from http://crystal-lang.org/docs/ # Copyright 2012-2018 Manas Technology Solutions. # Updated on May 17th, 2018 by @faustinoaq. require "http/server" server = HTTP::Server.new(8080) do |context| context.response.content_type = "text/plain" context.response.print "Hello world! The time is #{Time.now}" end puts "Listening on http://0.0.0.0:8080" server.listen module HTTP class RequestHandler end end alias NumericValue = Float32 | Float64 | Int32 | Int64 enum Time::DayOfWeek end class Greeting class_property global_greeting = "Hello world" @@default_greeting = "Hello world" def initialize(@custom_greeting = nil) end def print_greeting greeting = @custom_greeting || @@default_greeting puts greeting end end LUCKY_NUMBERS = [3, 7, 11] DOCUMENTATION_URL = "http://crystal-lang.org/docs" module Scorecard class Parser def parse(score_text) begin score_text.scan(SCORE_PATTERN) do |match| handle_match(match) end rescue err : ParseError # handle error ... end end end end module Money CURRENCIES = { "EUR" => 1.0, "ARS" => 10.55, "USD" => 1.12, "JPY" => 134.15, } class Amount getter :currency, :value def initialize(@currency, @value) end end class CurrencyConversion def initialize(@amount, @target_currency) end def amount # implement conversion ... end end end i = 0 while i < 10 proc = ->(x : Int32) do spawn do puts(x) end end proc.call(i) i += 1 end Fiber.yield # A buffered channel of capacity 2 channel = Channel(Int32).new(2) spawn do channel.send(1) channel.send(2) channel.send(3) end 3.times do |i| puts channel.receive end class MyDictionary(K, V) end MyBox.new(1) # :: MyBox(Int32) MyBox.new("hello") # :: MyBox(String) module Moo(T) def t T end end class Foo(U) include Moo(U) def initialize(@value : U) end end foo = Foo.new(1) foo.t # Int32 class Parent(T) end class Int32Child < Parent(Int32) end class GenericChild(T) < Parent(T) end class Person end a = 1 ptr = pointerof(a) ptr[100_000] = 2 # undefined behaviour, probably a segmentation fault alias Int32OrString = Int32 | String alias Int32OrNil = Int32? alias Int32OrNil_ = Int32 | ::Nil alias Int32Ptr = Int32* alias Int32Ptr_ = Pointer(Int32) alias Int32_8 = Int32[8] alias Int32_8_ = StaticArray(Int32, 8) alias Int32StringTuple = {Int32, String} alias Int32StringTuple_ = Tuple(Int32, String) alias Int32ToString = Int32 -> String alias Int32ToString_ = Proc(Int32, String) alias ProcThatReturnsInt32 = -> Int32 alias Int32AndCharToString = Int32, Char -> String alias ComplexProc = (Int32 -> Int32) -> String def foo(x : Int32) "instance" end def foo(x : Int32.class) "class" end foo 1 # "instance" foo Int32 # "class" class Parent end class Child1 < Parent end class Child2 < Parent end ary = [] of Parent.class ary << Child1 ary << Child2 # Same as not specifying a restriction, not very useful def foo(x : _) end # A bit more useful: any two arguments Proc that returns an Int32: def foo(x : _, _ -> Int32) end # alias SameAsInt32 = typeof(2) # alias Int32OrString_ = typeof(1, "a") class Person def initialize(name) @name = name @age = 0 end def name @name end def age @age end end john = Person.new "John" peter = Person.new "Peter" john.name # => "John" john.age # => 0 peter.name # => "Peter" class Person def self.new(name) instance = Person.allocate instance.initialize(name) instance end end if a.is_a?(String) # here a is a String end if b.is_a?(Number) # here b is a Number end a = some_condition ? 1 : "hello" # a : Int32 | String if a.is_a?(Number) # a : Int32 else # a : String end if a.is_a?(String) && b.is_a?(Number) # here a is a String and b is a Number end a.+(b) struct Vector2 getter x, y def initialize(@x, @y) end def +(other) Vector2.new(x + other.x, y + other.y) end end v1 = Vector2.new(1, 2) v2 = Vector2.new(3, 4) v1 + v2 # => Vector2(@x=4, @y=6) struct Vector2 def - Vector2.new(-x, -y) end end v1 = Vector2.new(1, 2) -v1 # => Vector2(@x=-1, @y=-2) class MyArray def [](index) # ... end def [](index1, index2, index3) # ... end def []=(index, value) # ... end end array = MyArray.new array[1] # invokes the first method array[1, 2, 3] # invokes the second method array[1] = 2 # invokes the third method array.[](1) # invokes the first method array.[](1, 2, 3) # invokes the second method array.[]=(1, 2) # invokes the third method raise "OH NO!" raise Exception.new("Some error") class MyException < Exception end begin raise MyException.new("OH NO!") rescue ex : MyException puts "Rescued MyException: #{ex.message}" end begin # ... rescue ex : MyException | MyOtherException # only MyException or MyOtherException rescue # any other kind of exception ensure puts "Cleanup..." end def some_method something_dangerous rescue # execute if an exception is raised end array = [1, 2, 3] array[4] # raises because of IndexError array[4]? # returns nil because of index out of bounds def some_proc(&block : Int32 -> Int32) block end x = 0 proc = ->(i : Int32) { x += i } proc = some_proc(&proc) proc.call(1) # => 1 proc.call(10) # => 11 x # => 11 def add(x, y) x + y end adder = ->add(Int32, Int32) adder.call(1, 2) # => 3 module Curses class Window end end Curses::Window.new 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 module Base64 extend self def encode64(string) # ... end def decode64(string) # ... end end Base64.encode64 "hello" # => "aGVsbG8=" if some_condition a = 1 else a = "hello" end a_as_int = a.as(Int32) a_as_int.abs # works, compiler knows that a_as_int is Int32 ptr = Pointer(Int32).malloc(1) ptr.as(Int8*) # :: Pointer(Int8) array = [1, 2, 3] # object_id returns the address of an object in memory, # so we create a pointer with that address ptr = Pointer(Void).new(array.object_id) # Now we cast that pointer to the same type, and # we should get the same value array2 = ptr.as(Array(Int32)) array2.same?(array) # => true a = 1 b = a.as(Int32 | Float64) b # :: Int32 | Float64 ary = [1, 2, 3] # We want to create an array 1, 2, 3 of Int32 | Float64 ary2 = ary.map { |x| x.as(Int32 | Float64) } ary2 # :: Array(Int32 | Float64) ary2 << 1.5 # OK class Person def initialize(@name) end def name @name end end a = [] of Person x = a.map { |f| f.name } # Error: can't infer block return type a = [] of Person x = a.map { |f| f.name.as(String) } # OK Person.new "John" a = [] of Person x = a.map { |f| f.name } # OK loop do do_something break if some_condition end class Point def initialize(@x, @y) end end Point.new 1, 2 # 2 x Int32 = 2 x 4 = 8 instance_sizeof(Point) # => 12 a = 1 while a < 5 a += 1 if a == 3 next end puts a end # The above prints the numbers 2, 4 and 5 lib C # In C: double cos(double x) fun cos(value : Float64) : Float64 fun getch : Int32 fun srand(seed : UInt32) fun exit(status : Int32) : NoReturn fun printf(format : UInt8*, ...) : Int32 end C.cos(1.5) # => 0.0707372 C.srand(1_u32) a = 1 b = 2 C.printf "%d + %d = %d\n", a, b, a + b lib LibSDL fun init = SDL_Init(flags : UInt32) : Int32 end lib LLVMIntrinsics fun ceil_f32 = "llvm.ceil.f32"(value : Float32) : Float32 end lib MyLib fun my_fun(some_size : LibC::SizeT) end @[Link("pcre")] lib LibPCRE end lib C {% if flag?(:x86_64) %} alias SizeT = UInt64 {% else %} alias SizeT = UInt32 {% end %} fun memcmp(p1 : Void*, p2 : Void*, size : C::SizeT) : Int32 end lib X enum SomeEnum Ten = 10 Twenty = 10 * 2 ThirtyTwo = 1 << 5 end end lib X enum SomeEnum A = 1_u32 end end X::SomeEnum::Zero # => 0_i8 X::SomeEnum::Two # => 2_i8 lib X fun callback(f : Int32 -> Int32) end f = ->(x : Int32) { x + 1 } X.callback(f) X.callback ->(x) { x + 1 } X.callback nil lib LibFoo fun store_callback(callback : ->) fun execute_callback end LibFoo.store_callback ->{ raise "OH NO!" } LibFoo.execute_callback lib LibFoo fun store_callback(callback : ->) @[Raises] fun execute_callback end @[Link("pcre")] lib PCRE INFO_CAPTURECOUNT = 2 end PCRE::INFO_CAPTURECOUNT # => 2 lib U # In C: # # union IntOrFloat { # int some_int; # double some_float; # }; union IntOrFloat some_int : Int32 some_float : Float64 end end value = U::IntOrFloat.new value = uninitialized U::IntOrFlaot value.some_int # => some garbage value value = U::IntOrFloat.new value.some_int = 1 value.some_int # => 1 value.some_float # => 4.94066e-324 def change_it(value) value.some_int = 1 end value = U::IntOrFloat.new change_it value value.some_int # => 0 lib C # In C: # # struct TimeZone { # int minutes_west; # int dst_time; # }; struct TimeZone minutes_west : Int32 dst_time : Int32 end end lib C # This is a forward declaration struct Node end struct Node node : Node* end end tz = C::TimeZone.new tz = uninitialized C::TimeZone tz.minutes_west # => some garbage value tz = C::TimeZone.new tz.minutes_west = 1 tz.minutes_west # => 1 tz = C::TimeZone.new minutes_west: 1, dst_time: 2 tz.minutes_west # => 1 tz.dst_time # => 2 def change_it(tz) tz.minutes_west = 1 end tz = C::TimeZone.new change_it tz tz.minutes_west # => 0 lib C $errno : Int32 end C.errno # => some value C.errno = 0 C.errno # => 0 lib C @[ThreadLocal] $errno : Int32 end lib C fun waitpid(pid : Int32, status_ptr : Int32*, options : Int32) : Int32 end status_ptr = uninitialized Int32 C.waitpid(pid, pointerof(status_ptr), options) C.waitpid(pid, out status_ptr, options) lib X type CInt = Int32 end {% if flag?(:x86_64) %} # some specific code for 64 bits platforms {% else %} # some specific code for non-64 bits platforms {% end %} {% if flag?(:linux) && flag?(:x86_64) %} # some specific code for linux 64 bits {% end %} lib C {% if flag?(:linux) && flag?(:x86_64) %} struct SomeStruct some_field : Int32 end {% else %} struct SomeStruct some_field : Int64 end {% end %} end # Assigns to a local variable local = 1 # Assigns to a global property class Global class_property global1 = 1 class_getter global2 = 2 class_setter global3 = 3 # Fails on nil class_property! global4 = 4 class_getter! global5 = 5 class_setter! global6 = 6 end class Testing # Assigns to an instance variable @instance = 2 # Assigns to a class variable @@class = 3 end local += 1 # same as: local = local + 1 # The above is valid with these operators: # +, -, *, /, %, |, &, ^, **, <<, >> local ||= 1 # same as: local || (local = 1) local &&= 1 # same as: local && (local = 1) # A setter person.name=("John") # The above can be written as: person.name = "John" # An indexed assignment objects.[]=(2, 3) # The above can be written as: objects[2] = 3 # Not assignment-related, but also syntax sugar: objects.[](2, 3) # The above can be written as: objects[2, 3] person.age += 1 # same as: person.age = person.age + 1 person.name ||= "John" # same as: person.name || (person.name = "John") person.name &&= "John" # same as: person.name && (person.name = "John") objects[1] += 2 # same as: objects[1] = objects[1] + 2 objects[1] ||= 2 # same as: objects[1]? || (objects[1] = 2) objects[1] &&= 2 # same as: objects[1]? && (objects[1] = 2) alias PInt32 = Pointer(Int32) ptr = PInt32.malloc(1) # : Pointer(Int32) alias RecArray = Array(Int32) | Array(RecArray) ary = [] of RecArray ary.push [1, 2, 3] ary.push ary ary # => [[1, 2, 3], [...]] module Json alias Type = Nil | Bool | Int64 | Float64 | String | Array(Type) | Hash(String, Type) end a = 1 if a > 0 a = 10 end a # => 10 b = 1 if b > 2 b = 10 else b = 20 end b # => 20 if some_condition do_something elsif some_other_condition do_something_else else do_that end a = 1 if some_condition a = "hello" else a = true end # a : String | Bool b = 1 if some_condition b = "hello" end # b : Int32 | String if some_condition c = 1 else c = "hello" end # c : Int32 | String if some_condition d = 1 end # d : Int32 | Nil a = 1 if some_condition a = "hello" # a : String a.size end # a : String | Int32 if some_condition e = 1 else e = "hello" # e : String return end # e : Int32 enum Color : UInt8 Red # 0 Green # 1 Blue = 5 # overwritten to 5 Yellow # 6 (5 + 1) def red? self == Color::Red end end Color::Red.value # :: UInt8 @[Flags] enum IOMode Read # 1 Write # 2 Async # 4 end IOMode::None.value # => 0 IOMode::All.value # => 7 puts(Color::Red) # prints "Red" puts(IOMode::Write | IOMode::Async) # prints "Write, Async" puts Color.new(1) # => prints "Green" puts Color.new(10) # => prints "10" Color::Red.red? # => true Color::Blue.red? # => false def paint(color : Color) case color when Color::Red # ... else # Unusual, but still can happen raise "unknown color: #{color}" end end paint Color::Red def paint(color : Symbol) case color when :red # ... else raise "unknown color: #{color}" end end paint :red name = "Crystal" age = 1 flower = "Tulip" # At this point 'flower' is a String flower = 1 # At this point 'flower' is an Int32 class Foo def finalize # Invoked when Foo is garbage-collected puts "Bye bye from #{self}!" end end # Prints "Bye bye ...!" for ever loop do Foo.new end # Defines a method in the program def add(x, y) x + y end # Invokes the add method in the program add(1, 2) # => 3 def even?(num) if num % 2 == 0 return true end return false end def add(x, y) x + y end class Foo def bar # invokes the program's add method add(1, 2) # invokes Foo's baz method baz(1, 2) end def baz(x, y) x * y end end def baz(x, y) x + y end class Foo def bar baz(4, 2) # => 2 ::baz(4, 2) # => 6 end def baz(x, y) x - y end end x = 1 def add(y) x + y # error: undefined local variable or method 'x' end add(2) add 1, 2 # same as add(1, 2) class Counter @@instances = 0 def initialize @@instances += 1 end def self.instances @@instances end end Counter.instances # => 0 Counter.new Counter.new Counter.new Counter.instances # => 3 class Counter def self.increment @@instances += 1 end end Counter.increment # Error: undefined method '+' for Nil class Parent @@counter = 0 end class Child < Parent def self.counter @@counter end end Child.counter # => nil unless some_condition then_expression else else_expression end # Can also be written as a suffix close_door unless door_closed? a = 1 b = typeof(a) # => Int32 typeof(1, "a", 'a') # => (Int32 | String | Char) hash = {} of Int32 => String another_hash = typeof(hash).new # :: Hash(Int32, String) class Array def self.elem_type(typ) if typ.is_a?(Array) elem_type(typ.first) else typ end end end nest = [1, ["b", [:c, ['d']]]] flat = Array(typeof(Array.elem_type(nest))).new typeof(nest) # => Array(Int32 | Array(String | Array(Symbol | Array(Char)))) typeof(flat) # => Array(String | Int32 | Symbol | Char) a = 2 if some_condition x = 0 proc = ->{ x += 1; x } proc.call # => 1 proc.call # => 2 x # => 2 def counter x = 0 ->{ x += 1; x } end proc = counter proc.call # => 1 proc.call # => 2 def foo yield end x = 1 foo do x = "hello" end x # : Int32 | String x = 1 foo do x = "hello" end x # : Int32 | String x = 'a' x # : Char def capture(&block) block end x = 1 capture { x = "hello" } x = 'a' x # : Int32 | String | Char def capture(&block) block end x = 1 ->{ x = "hello" } x = 'a' x # : Int32 | String | Char abstract class Animal # Makes this animal talk abstract def talk end class Dog < Animal def talk "Woof!" end end class Cat < Animal def talk "Miau" end end class Person getter pet def initialize(@name, @pet) end end john = Person.new "John", Dog.new peter = Person.new "Peter", Cat.new john.pet.talk # => "Woof!" a = 1 > 2 ? 3 : 4 # The above is the same as: a = if 1 > 2 3 else 4 end def some_method : String "hello" end PI = 3.14 module Earth RADIUS = 6_371_000 end PI # => 3.14 Earth::RADIUS # => 6_371_000 TEN = begin a = 0 while a < 10 a += 1 end a end TEN # => 10 class Person getter name def initialize(@name) @age = 0 end end john = Person.new "John" john.name # => "John" john.name.size # => 4 one = Person.new 1 one.name # => 1 one.name + 2 # => 3 john = Person.new "John" one = Person.new 1 john = Person.new "John" one = Person.new 1 # Error: undefined method 'size' for Int32 john.name.size # Error: no overload matches 'String#+' with types Int32 john.name + 3 john = Person.new "John" john.name.size one = Person.new 1 class Person getter name def initialize(@name) @age = 0 end def address @address end def address=(@address) end end john = Person.new "John" john.address = "Argentina" # Error: undefined method 'size' for Nil john.address.size class Person @age = 0 def initialize(@name) end end class Person @age : Int32 def initialize(@name) @age = 0 end end a = if 2 > 1 3 else 4 end a # => 3 if 1 > 2 else 3 end def twice(&block) yield yield end twice() do puts "Hello!" end twice do puts "Hello!" end twice { puts "Hello!" } def twice yield 1 yield 2 end twice do |i| puts "Got #{i}" end twice { |i| puts "Got #{i}" } def many yield 1, 2, 3 end many do |x, y, z| puts x + y + z end # Output: 6 def many yield 1, 2, 3 end many do |x, y| puts x + y end # Output: 3 def twice yield yield end twice do |i| puts i.inspect end def some yield 1, 'a' yield true, "hello" yield 2 end some do |first, second| # first is Int32 | Bool # second is Char | String | Nil end method do |argument| argument.some_method end method(&.some_method) method &.some_method(arg1, arg2) method &.+(2) method &.[index] def twice v1 = yield 1 puts v1 v2 = yield 2 puts v2 end twice do |i| i + 1 end ary = [1, 2, 3] ary.map { |x| x + 1 } # => [2, 3, 4] ary.select { |x| x % 2 == 1 } # => [1, 3] def transform(value) yield value end transform(1) { |x| x + 1 } # => 2 def thrice puts "Before 1" yield 1 puts "Before 2" yield 2 puts "Before 3" yield 3 puts "After 3" end thrice do |i| if i == 2 break end end def twice yield 1 yield 2 end twice { |i| i + 1 } # => 3 twice { |i| break "hello" } # => "hello" value = twice do |i| if i == 1 break "hello" end i + 1 end value # :: Int32 | String values = twice { break 1, 2 } values # => {1, 2} value = twice { break } value # => nil def twice yield 1 yield 2 end twice do |i| if i == 1 puts "Skipping 1" next end puts "Got #{i}" end def twice v1 = yield 1 puts v1 v2 = yield 2 puts v2 end twice do |i| if i == 1 next 10 end i + 1 end # Output # 10 # 3 class Foo def one 1 end def yield_with_self with self yield end def yield_normally yield end end def one "one" end Foo.new.yield_with_self { one } # => 1 Foo.new.yield_normally { one } # => "one" def twice yield 1 yield 2 end twice do |i| puts "Got: #{i}" end i = 1 puts "Got: #{i}" i = 2 puts "Got: #{i}" 3.times do |i| puts i end struct Int def times i = 0 while i < self yield i i += 1 end end end i = 0 while i < 3 puts i i += 1 end class Person def initialize(@name) end def greet puts "Hi, I'm #{@name}" end end class Employee < Person end employee = Employee.new "John" employee.greet # "Hi, I'm John" class Person def initialize(@name) end end class Employee < Person def initialize(@name, @company_name) end end Employee.new "John", "Acme" # OK Employee.new "Peter" # Error: wrong number of arguments # for 'Employee:Class#new' (1 for 2) class Person def greet(msg) puts "Hi, #{msg}" end end class Employee < Person def greet(msg) puts "Hello, #{msg}" end end p = Person.new p.greet "everyone" # "Hi, everyone" e = Employee.new e.greet "everyone" # "Hello, everyone" class Person def greet(msg) puts "Hi, #{msg}" end end class Employee < Person def greet(msg : Int32) puts "Hi, this is a number: #{msg}" end end e = Employee.new e.greet "everyone" # "Hi, everyone" e.greet 1 # "Hi, this is a number: 1" class Person def greet(msg) puts "Hello, " # {msg}" end end class Employee < Person def greet(msg) super # Same as: super(msg) super("another message") end end def int_to_int(&block : Int32 -> Int32) block end proc = int_to_int { |x| x + 1 } proc.call(1) # => 2 class Model def on_save(&block) @on_save_callback = block end def save if callback = @on_save_callback callback.call end end end model = Model.new model.on_save { puts "Saved!" } model.save # prints "Saved!" def some_proc(&block : Int32 ->) block end proc = some_proc { |x| x + 1 } proc.call(1) # void def some_proc(&block : Int32 -> _) block end proc = some_proc { |x| x + 1 } proc.call(1) # 2 proc = some_proc { |x| x.to_s } proc.call(1) # "1" macro update_x x = 1 end x = 0 update_x x # => 1 macro dont_update_x %x = 1 puts %x end x = 0 dont_update_x # outputs 1 x # => 0 macro fresh_vars_sample(*names) # First declare vars {% for name, index in names %} print "Declaring: ", "%name{index}", '\n' %name{index} = {{index}} {% end %} # Then print them {% for name, index in names %} print "%name{index}: ", %name{index}, '\n' {% end %} end fresh_vars_sample a, b, c # Sample output: # Declaring: __temp_255 # Declaring: __temp_256 # Declaring: __temp_257 # __temp_255: 0 # __temp_256: 1 # __temp_257: 2 class Object macro instance_vars_names def instance_vars_names : Array(String) {{ @type.instance_vars.map &.name.stringify }} end end end class Person def initialize(@name, @age) end end person = Person.new "John", 30 person.instance_vars_names # => ["name", "age"] class Object macro has_instance_var?(name) def has_instance_var?(name) : Bool # We cannot access name inside the macro expansion here, # instead we need to use the macro language to construct an array # and do the inclusion check at runtime. {{ @type.instance_vars.map &.name.stringify }}.includes? name end end end person = Person.new "John", 30 person.has_instance_var?("name") # => true person.has_instance_var?("birthday") # => false class Parent macro inherited def {{@type.name.downcase.id}} 1 end end end class Child < Parent end Child.new.child # => 1 macro method_missing(name, args, block) print "Got ", {{name.id.stringify}}, " with ", {{args.size}}, " arguments", '\n' end foo # Prints: Got foo with 0 arguments bar 'a', 'b' # Prints: Got bar with 2 arguments sizeof(Int32) # => 4 sizeof(Int64) # => 8 # On a 64 bits machine sizeof(Pointer(Int32)) # => 8 sizeof(String) # => 8 a = 1 sizeof(typeof(a)) # => 4 class Foo macro emphasize(value) "***#{ {{value}} }***" end def yield_with_self with self yield end end Foo.new.yield_with_self { emphasize(10) } # => "***10***" # This generates: # # def :foo # 1 # end define_method :foo, 1 macro define_method(name, content) def {{name.id}} {{content}} end end # This correctly generates: # # def foo # 1 # end define_method :foo, 1 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 {% if env("TEST") %} puts "We are in test mode" {% end %} 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 macro define_dummy_methods(hash) {% for key, value in hash %} def {{key.id}} {{value}} end {% end %} end define_dummy_methods({foo: 10, bar: 20}) foo # => 10 bar # => 20 {% for name, index in ["foo", "bar", "baz"] %} def {{name.id}} {{index}} end {% end %} foo # => 0 bar # => 1 baz # => 2 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 macro println(*values) print {{*values}}, '\n' end println 1, 2, 3 # outputs 123\n VALUES = [1, 2, 3] {% for value in VALUES %} puts {{value}} {% end %} until some_condition do_this end # The above is the same as: while !some_condition do_this end a = some_condition ? nil : 3 # a is Int32 or Nil if a # Since the only way to get here is if a is truthy, # a can't be nil. So here a is Int32. a.abs end if a = some_expression # here a is not nil end if a && b # here both a and b are guaranteed not to be Nil end if @a # here @a can be nil end # First option: assign it to a variable if a = @a # here a can't be nil end # Second option: use `Object#try` found in the standard library @a.try do |a| # here a can't be nil end if method # first call to a method that can return Int32 or Nil # here we know that the first call did not return Nil method # second call can still return Int32 or Nil end class Person def become_older(by = 1) @age += by end end john = Person.new "John" john.age # => 0 john.become_older john.age # => 1 john.become_older 2 john.age # => 3 john.become_older by: 5 def some_method(x, y = 1, z = 2, w = 3) # do something... end some_method 10 # x = 10, y = 1, z = 2, w = 3 some_method 10, z: 10 # x = 10, y = 1, z = 10, w = 3 some_method 10, w: 1, y: 2, z: 3 # x = 10, y = 2, z = 3, w = 1 case exp when value1, value2 do_something when value3 do_something_else else do_another_thing end case var when String # var : String do_something when Int32 # var : Int32 do_something_else else # here var is neither a String nor an Int32 do_another_thing end case num when .even? do_something when .odd? do_something_else end case when cond1, cond2 do_something when cond3 do_something_else end a = 1 a.responds_to?(:abs) # => true a.responds_to?(:size) # => false foo_or_bar = /foo|bar/ heeello = /h(e+)llo/ integer = /\d+/ r = /foo/imx slash = /\// r = %r(regex with slash: /) "hello world" "\"" # double quote "\\" # backslash "\e" # escape "\f" # form feed "\n" # newline "\r" # carriage return "\t" # tab "\v" # vertical tab "\101" # == "A" "\123" # == "S" "\12" # == "\n" "\1" # string with one character with code point 1 "\u0041" # == "A" "\u{41}" # == "A" "\u{1F52E}" "hello world" # same as "hello\n world" "hello " \ "world, " \ "no newlines" # same as "hello world, no newlines" "hello \ world, \ no newlines" # same as "hello world, no newlines" %(hello ("world")) # => "hello (\"world\")" %[hello ["world"]] # => "hello [\"world\"]" %{hello {"world"}} # => "hello {\"world\"}" %> # => "hello <\"world\">" %|hello "world"| # => "hello \"world\"" <<-XML XML # Same as "Hello\n world" <<-STRING Hello world STRING # Same as " Hello\n world" <<-STRING Hello world STRING a = 1 b = 2 "sum = #{a + b}" # "sum = 3" 1.0 # Float64 1.0_f32 # Float32 1_f32 # Float32 1e10 # Float64 1.5e10 # Float64 1.5e-7 # Float64 +1.3 # Float64 -0.5 # Float64 1_000_000.111_111 # better than 1000000.111111 'a' 'z' '0' '_' "あ" '\'' # single quote '\\' # backslash '\e' # escape '\f' # form feed '\n' # newline '\r' # carriage return '\t' # tab '\v' # vertical tab "\101" # == 'A' "\123" # == 'S' "\12" # == '\n' "\1" # code point 1 '\u0041' # == 'A' '\u{41}' # == 'A' '\u{1F52E}' {1 => 2, 3 => 4} # Hash(Int32, Int32) {1 => 2, 'a' => 3} # Hash(Int32 | Char, Int32) {} of Int32 => Int32 # same as Hash(Int32, Int32).new {key1: 'a', key2: 'b'} # Hash(Symbol, Char) {"key1": 'a', "key2": 'b'} # Hash(String, Char) MyType{"foo" => "bar"} tmp = MyType.new tmp["foo"] = "bar" tmp tmp = MyType(typeof("foo"), typeof("bar")).new tmp["foo"] = "bar" tmp MyType(String, String){"foo" => "bar"} :hello :good_bye # With spaces and symbols :"symbol with spaces" # Ending with question and exclamation marks :question? :exclamation! # For the operators :+ :- :* :/ :== :< :<= :> :>= :! :!= :=~ :!~ :& :| :^ :~ :** :>> :<< :% :[] :[]? :[]= :<=> :=== x..y # an inclusive range, in mathematics: [x, y] x...y # an exclusive range, in mathematics: [x, y) # 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) Proc(Int32, String).new { |x| x.to_s } # Proc(Int32, String) proc = ->(x : Int32, y : Int32) { x + y } proc.call(1, 2) # => 3 def one 1 end proc = ->one proc.call # => 1 def plus_one(x) x + 1 end proc = ->plus_one(Int32) proc.call(41) # => 42 str = "hello" proc = ->str.count(Char) proc.call('e') # => 1 proc.call('l') # => 2 tuple = {1, "hello", 'x'} # Tuple(Int32, String, Char) tuple[0] # => 1 (Int32) tuple[1] # => "hello" (String) tuple[2] # => 'x' (Char) [1, 2, 3] # Array(Int32) [1, "hello", 'x'] # Array(Int32 | String | Char) [] of Int32 # same as Array(Int32).new %w(one two three) # ["one", "two", "three"] %i(one two three) # [:one, :two, :three] MyType{1, 2, 3} tmp = MyType.new tmp << 1 tmp << 2 tmp << 3 tmp tmp = MyType(typeof(1, 2, 3)).new tmp << 1 tmp << 2 tmp << 3 tmp MyType(Int32 | String){1, 2, "foo"} nil 1 # Int32 1_i8 # Int8 1_i16 # Int16 1_i32 # Int32 1_i64 # Int64 1_u8 # UInt8 1_u16 # UInt16 1_u32 # UInt32 1_u64 # UInt64 +10 # Int32 -20 # Int32 2147483648 # Int64 9223372036854775808 # UInt64 1_000_000 # better than 1000000 0b1101 # == 13 0o123 # == 83 0xFE012D # == 16646445 0xfe012d # == 16646445 true # A Bool that is true false # A Bool that is false a = 1 ptr = pointerof(a) ptr.value = 2 a # => 2 class Point def initialize(@x, @y) end def x @x end def x_ptr pointerof(@x) end end point = Point.new 1, 2 ptr = point.x_ptr ptr.value = 10 point.x # => 10 def add(x : Number, y : Number) x + y end # Ok add 1, 2 # Ok # Error: no overload matches 'add' with types Bool, Bool add true, false def add(x, y) x + y end add true, false # A class that has a + method but isn't a Number class Six def +(other) 6 + other end end # add method without type restrictions def add(x, y) x + y end # OK add Six.new, 10 # add method with type restrictions def restricted_add(x : Number, y : Number) x + y end # Error: no overload matches 'restricted_add' with types Six, Int32 restricted_add Six.new, 10 class Person def ==(other : self) other.name == name end def ==(other) false end end john = Person.new "John" another_john = Person.new "John" peter = Person.new "Peter" john == another_john # => true john == peter # => false (names differ) john == 1 # => false (because 1 is not a Person) class Person def self.compare(p1 : self, p2 : self) p1.name == p2.name end end john = Person.new "John" peter = Person.new "Peter" Person.compare(john, peter) # OK def foo(x : Int32) end foo 1 # OK foo "hello" # Error def foo(x : Int32.class) end foo Int32 # OK foo String # Error def foo(x : Int32.class) puts "Got Int32" end def foo(x : String.class) puts "Got String" end foo Int32 # prints "Got Int32" foo String # prints "Got String" def foo(*args : Int32) end def foo(*args : String) end foo 1, 2, 3 # OK, invokes first overload foo "a", "b", "c" # OK, invokes second overload foo 1, 2, "hello" # Error foo() # Error def foo # This is the empty-tuple case end def foo(x : T) T end foo(1) # => Int32 foo("hello") # => String def foo(x : Array(T)) T end foo([1, 2]) # => Int32 foo([1, "a"]) # => (Int32 | String) def foo(x : T.class) Array(T) end foo(Int32) # => Array(Int32) foo(String) # => Array(String) class Person # Increases age by one def become_older @age += 1 end # Increases age by the given number of years def become_older(years : Int32) @age += years end # Increases age by the given number of years, as a String def become_older(years : String) @age += years.to_i end # Yields the current age of this person and increases # its age by the value returned by the block def become_older @age += yield @age end end person = Person.new "John" person.become_older person.age # => 1 person.become_older 5 person.age # => 6 person.become_older "12" person.age # => 18 person.become_older do |current_age| current_age < 20 ? 10 : 30 end person.age # => 28 a = 1 a.is_a?(Int32) # => true a.is_a?(String) # => false a.is_a?(Number) # => true a.is_a?(Int32 | String) # => true # One for each thread @[ThreadLocal] values = [] of Int32 @[AlwaysInline] def foo 1 end @[NoInline] def foo 1 end lib LibFoo @[CallConvention("X86_StdCall")] fun foo : Int32 end def sum(*elements) total = 0 elements.each do |value| total += value end total end # elements is Tuple(Int32, Int32, Int32, Float64) sum 1, 2, 3, 4.5 if a.responds_to?(:abs) # here a's type will be reduced to those responding to the 'abs' method end a = some_condition ? 1 : "hello" # a : Int32 | String if a.responds_to?(:abs) # here a will be Int32, since Int32#abs exists but String#abs doesn't else # here a will be String end if (a = @a).responds_to?(:abs) # here a is guaranteed to respond to `abs` end def capture(&block) block end def invoke(&block) block.call end proc = capture { puts "Hello" } invoke(&proc) # prints "Hello" def capture(&block) block end def twice yield yield end proc = capture { puts "Hello" } twice &proc twice &->{ puts "Hello" } def say_hello puts "Hello" end twice &->say_hello def foo yield 1 end def wrap_foo puts "Before foo" foo do |x| yield x end puts "After foo" end wrap_foo do |i| puts i end def foo yield 1 end def wrap_foo(&block : Int32 -> _) puts "Before foo" foo(&block) puts "After foo" end wrap_foo do |i| puts i end foo_forward do |i| break # error end a = 2 while (a += 1) < 20 if a == 10 # goes to 'puts a' break end end puts a # => 10 class Person private def say(message) puts message end def say_hello say "hello" # OK, no receiver self.say "hello" # Error, self is a receiver other = Person.new "Other" other.say "hello" # Error, other is a receiver end end class Employee < Person def say_bye say "bye" # OK end end module Namespace class Foo protected def foo puts "Hello" end end class Bar def bar # Works, because Foo and Bar are under Namespace Foo.new.foo end end end Namespace::Bar.new.bar class Person protected def self.say(message) puts message end def say_hello Person.say "hello" end end buffer = uninitialized UInt8[256] foo = rand(5) > 1 ? 1 : nil foo.not_nil!.to_i