authors/asterite.jpg Ary Borenzweig 02 Apr 2016

Crystal 0.15.0 released!

Crystal 0.15.0 has been released!

This is a small release where the most significant change is that now ! and nil? have their meaning hardcoded in the language. To understand why this is important let’s look at a simple example.

nil? and !

The task is to write a program that prints the character number of an occurrence of a string inside another string, or print “Not found” if not found.

One way to solve this is:

# `String#index` returns an Int32 if the index is found, or Nil if not
index = "some_word".index(ARGV[0])
if index
  puts "Found at character number #{index + 1}"
else
  puts "Not found"
end

Note that in the above program the first character, for the user, is 1, the second is 2, etc. That’s why we need to add 1 to the index.

The compiler understands that if index is truthy (not nil nor false nor a null pointer) then inside the then branch index can only be Int32 so the operation index + 1 compiles fine.

Another way to write the same program is this:

index = "some_word".index(ARGV[0])
if !index
  puts "Not found"
else
  puts "Found at character number #{index + 1}"
end

If you compile the above program in a previous version you will get an error:

Error in ./file.cr:14: undefined method '+' for Nil (compile-time type is Int32?)

  puts "Found at character number #{index + 1}"
                                          ^

In this case it would seem obvious that in the else branch there’s no possible way index can be nil. However, because ! was a regular method and anyone could redefine its meaning, the compiler couldn’t make this guarantee.

Now ! has its meaning hardcoded, which means that the last program compiles just fine.

Yet another way to write the same program is this:

index = "some_word".index(ARGV[0])
if index.nil?
  puts "Not found"
else
  puts "Found at character number #{index + 1}"
end

As before, if you compile this with a previous version you will get the same error, for the same reason: you could redefine the meaning of nil?.

Starting from 0.15.0 nil? has its meaning hardcoded, so the above program again works just fine.

This is super important because now the language is much more intuitive and correct. And there are many Ruby programmers that use nil?, a lot, and if they start using Crystal there won’t be any surprises in their expectations about what the compiler should understand in these cases.

Some others ways to write this program:

# 1
if !index.nil?
  puts "Found at character number #{index + 1}"
else
  puts "Not found"
end

# 2
if index.is_a?(Int32)
  puts "Found at character number #{index + 1}"
else
  puts "Not found"
end

# 3
if !index.is_a?(Int32)
  puts "Not found"
else
  puts "Found at character number #{index + 1}"
end

All of the above will now work just fine, as one would expect.

We originally considered removing nil? from the standard library and language, but Nil is such a fundamental type that it makes sense to have good support for handling it.

And remember: nil is not a billion dollar mistake if the compiler takes care of making sure you always handle it.

Better Playground

The playground (crystal play) had many bug fixes, stablizations and improvements. For example runtime exceptions are now pointed out in the source code.

There’s also a new “Workbook” tab/section that can list files you put in a playground directory, to use it as a tutorial for others, or maybe when giving a talk. These files can be HTML, Markdown or Crystal files.

For example, @will gave a talk about Crystal a few days ago at Ruby on Ales and used it to introduce the language. Make sure to check out his repo and try it out.

Crystal 0.15.0 has been released!

This is a small release where the most significant change is that now ! and nil? have their meaning hardcoded in the language. To understand why this is important let’s look at a simple example.

nil? and !

The task is to write a program that prints the character number of an occurrence of a string inside another string, or print “Not found” if not found.

One way to solve this is:

# `String#index` returns an Int32 if the index is found, or Nil if not
index = "some_word".index(ARGV[0])
if index
  puts "Found at character number #{index + 1}"
else
  puts "Not found"
end

Note that in the above program the first character, for the user, is 1, the second is 2, etc. That’s why we need to add 1 to the index.

The compiler understands that if index is truthy (not nil nor false nor a null pointer) then inside the then branch index can only be Int32 so the operation index + 1 compiles fine.

Another way to write the same program is this:

index = "some_word".index(ARGV[0])
if !index
  puts "Not found"
else
  puts "Found at character number #{index + 1}"
end

If you compile the above program in a previous version you will get an error:

Error in ./file.cr:14: undefined method '+' for Nil (compile-time type is Int32?)

  puts "Found at character number #{index + 1}"
                                          ^

In this case it would seem obvious that in the else branch there’s no possible way index can be nil. However, because ! was a regular method and anyone could redefine its meaning, the compiler couldn’t make this guarantee.

Now ! has its meaning hardcoded, which means that the last program compiles just fine.

Yet another way to write the same program is this:

index = "some_word".index(ARGV[0])
if index.nil?
  puts "Not found"
else
  puts "Found at character number #{index + 1}"
end

As before, if you compile this with a previous version you will get the same error, for the same reason: you could redefine the meaning of nil?.

Starting from 0.15.0 nil? has its meaning hardcoded, so the above program again works just fine.

This is super important because now the language is much more intuitive and correct. And there are many Ruby programmers that use nil?, a lot, and if they start using Crystal there won’t be any surprises in their expectations about what the compiler should understand in these cases.

Some others ways to write this program:

# 1
if !index.nil?
  puts "Found at character number #{index + 1}"
else
  puts "Not found"
end

# 2
if index.is_a?(Int32)
  puts "Found at character number #{index + 1}"
else
  puts "Not found"
end

# 3
if !index.is_a?(Int32)
  puts "Not found"
else
  puts "Found at character number #{index + 1}"
end

All of the above will now work just fine, as one would expect.

We originally considered removing nil? from the standard library and language, but Nil is such a fundamental type that it makes sense to have good support for handling it.

And remember: nil is not a billion dollar mistake if the compiler takes care of making sure you always handle it.

Better Playground

The playground (crystal play) had many bug fixes, stablizations and improvements. For example runtime exceptions are now pointed out in the source code.

There’s also a new “Workbook” tab/section that can list files you put in a playground directory, to use it as a tutorial for others, or maybe when giving a talk. These files can be HTML, Markdown or Crystal files.

For example, @will gave a talk about Crystal a few days ago at Ruby on Ales and used it to introduce the language. Make sure to check out his repo and try it out.

comments powered by Disqus