2551 lines
35 KiB
Crystal
2551 lines
35 KiB
Crystal
# 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"| # => "hello \"world\""
|
|
|
|
<<-XML
|
|
<parent>
|
|
<child />
|
|
</parent>
|
|
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
|