32
loading...
This website collects cookies to deliver better user experience
#!/usr/bin/env ruby
# FizzBuzz in Crystal. Also in Ruby.
(1..100).each do |n|
if n % 15 == 0
puts "FizzBuzz"
elsif n % 3 == 0
puts "Fizz"
elsif n % 5 == 0
puts "Buzz"
else
puts n
end
end
#!/usr/bin/env crystal
# FizzBuzz in Crystal. Also in Ruby.
(1..100).each do |n|
if n % 15 == 0
puts "FizzBuzz"
elsif n % 3 == 0
puts "Fizz"
elsif n % 5 == 0
puts "Buzz"
else
puts n
end
end
$ crystal build fizzbuzz.cr
$ ./fizzbuzz
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
...
crystal build --release
. This will take a while.def add(a, b)
puts "#{a} + #{b} = #{a + b}"
end
add 400, 20
add 6, 0.9
add "foo", "bar"
$ crystal add.cr
400 + 20 = 420
6 + 0.9 = 6.9
foo + bar = foobar
def add(a, b)
puts "#{a} + #{b} = #{a + b}"
end
add 400, 20
add 6, 0.9
add "foo", "bar"
add "y", 2000
$ crystal add2.cr
Showing last frame. Use --error-trace for full trace.
In add2.cr:2:27
2 | puts "#{a} + #{b} = #{a + b}"
^
Error: no overload matches 'String#+' with type Int32
Overloads are:
- String#+(other : self)
- String#+(char : Char)
def add(a, b)
, Crystal doesn't immediately try to assign most generic possible type to it, like pretty much every other statically typed language. Instead, it parks that question, and when add
is actually used, it checks if it makes sense there.$ crystal build add.cr
$ objdump -t add | grep '\*add<'
0000000100012dd0 l d *UND* _*add<Int32, Int32>:Nil
0000000100012e70 l d *UND* _*add<Int32, Float64>:Nil
0000000100012ea0 l d *UND* _*add<String, String>:Nil
0000000100012e70 g F __TEXT,__text _*add<Int32, Float64>:Nil
0000000100012dd0 g F __TEXT,__text _*add<Int32, Int32>:Nil
0000000100012ea0 g F __TEXT,__text _*add<String, String>:Nil
nil
s (or null
s in most other languages). nil
s are pretty much necessary evil. A lot of code really wants data to be uninitialized for a while, and it would require a lot of twisting it around to always prevent this situation. On the other hand, a lot of other code wants to guarantee that it never gets nil
s, just actual values.ary = [1, 2, 3, nil]
puts "Type of array:"
puts typeof(ary)
puts ""
puts "Type of each element:"
ary.each do |x|
puts typeof(x)
end
puts ""
puts "Type of each element:"
ary.each do |x|
if x
puts typeof(x)
else
puts typeof(x)
end
end
$ crystal run ./types.cr
Type of array:
Array(Int32 | Nil)
Type of each element:
(Int32 | Nil)
(Int32 | Nil)
(Int32 | Nil)
(Int32 | Nil)
Type of each element:
Int32
Int32
Int32
Nil
ary
as array with some stuff in it. We ask Crystal what it thinks type of ary
is, and it correctly says it's Array(Int32 | Nil)
. Int32 | Nil
is union type. So far so good.typeof
operator is not any kind of hacky runtime check, it tells us what we know about the variable statically. So as all we know is that ary
is Array(Int32 | Nil)
, then Crystal can only tell us that each element is (Int32 | Nil)
.if/else
, Crystal checks which types can possibly get into each branch. So it knows for a fact in the positive branch, only Int32
can get there, and in the negative branch, only Nil
! This of course isn't some magic, and only some kinds of syntax trigger this splitting, but it's totally amazing in practice.nil
s, but it also works for any other union types.ary = [7, 210, "foo", nil]
puts "Type of array:"
puts typeof(ary)
puts ""
puts "Double each element:"
ary.each do |x|
puts "Double of #{x} (#{typeof(x)}) is #{x * 2}"
end
$ crystal run ./union.cr
Showing last frame. Use --error-trace for full trace.
In union.cr:9:46
9 | puts "Double of #{x} (#{typeof(x)}) is #{x * 2}"
^
Error: undefined method '*' for Nil (compile-time type is (Int32 | String | Nil))
ary = [7, 210, "foo", nil]
puts "Type of array:"
puts typeof(ary)
puts ""
puts "Double each element:"
ary.each do |x|
x ||= "unknown"
puts "Double of #{x} (#{typeof(x)}) is #{x * 2}"
end
crystal run ./union2.cr
Type of array:
Array(Int32 | String | Nil)
Double each element:
Double of 7 ((Int32 | String)) is 14
Double of 210 ((Int32 | String)) is 420
Double of foo ((Int32 | String)) is foofoo
Double of unknown ((Int32 | String)) is unknownunknown
x
cannot be nil
, so it runs perfectly.ary = [] of String
ary << "foo"
ary << "bar"
puts ary.map(&.upcase)
$ crystal run ./ary.cr
["FOO", "BAR"]
&.methodname
syntax for a trivial block - and it's part of language syntax. In Ruby it's &:methodname
(which started as a hack to turn Symbol
into Proc
).ary
is Array(String)
and treat ary << nil
as a type error:ary = ["foo"] of (String | Nil)
ary << "foo"
ary << nil
puts ary.map{|x| x || "unknown"}