Brian J. Cardiff 30 Jan 2019

Crystal 0.27.1 released!

Crystal 0.27.1 has been released!

This release includes new features and improvements in compiler errors that will be useful for many scenarios: scientific, web apps, cli, future advent of codes, you name it. There were 202 commits since 0.27.0 by 33 contributors.

This release ended up being bigger than expected, and we decided to close it at a point that made sense, and defer some WIP for the next version. There are a couple of breaking changes waiting to be merged already to start the process of 0.28.

Let’s review some of the most relevant changes in this release. But don’t miss the rest of the release changelog with a lot of valuable information.

Language changes

You already know tuples {1, "foo"}. We now allow a trailing comma as in {1, "foo", } to be a valid tuple. This comes in really handy when the tuple is generated from macros, for example. More on this at #7182.

Macros

There is a new macro that allows you to embed the content of a text file as a string literal during compile time. Neat for embedding text resources. This new macro comes in two flavors:

  • read_file(filename) that will raise a compile time error if the file does not exist.
  • read_file?(filename) that will generate a nil value if the file does not exist.

You can read about more about at #6967 and #7094 since there was some rethinking after the first PR and in the docs.

Numeric

The compiler is now able to emit overflow checks, but for now this feature is only available with an opt-in flag, to allow a migration path and feedback. In a future version, the overflow checks will be the default. When you build a program you can pass -D preview_overflow to enable them. Depending on your setup you might also need to use -D compiler_rt until we ship the compiler-rt dependency properly.

Assuming the overflow checks are turned on, methods like +, -, *, **, to_i8 and Int32.new will raise an OverflowError if the result doesn’t fit within the representable values. This applies to all integer types (including 128).

In case you need wrapping (a.k.a.: previous) behaviour you will need to use &+, &-, &*, &**, to_i8!, Int32.new!.

Many parts of the stdlib and the compiler had been updated to explicit use &+ (like Hasher) but if we happened to miss any, it would be great to hear about it. Check your apps with -D preview_overflow and let us know if you found any issues (before the overflow is the default behaviour).

You can read more about that at #7206 where you will also find other PRs related to it.

While implementing that, we also took care of some leftovers related to Int128 support like #7135

Also, one of those big little changes that bring joy to some of you, is that you are now able to use BigInt.new("1_000") to mimic 1_000. Read more at #7107.

Collections

There were many additions to Iterator, Enumerable that will come in handy in the next advent of code, and when processing collections of data.

Let’s see some quick examples of these additions. For Iterator#slice_after added by #7146:

ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]
ary.slice_after(&.even?).each { |x| puts x }
# output:
# [1, 3, 5, 8]
# [10]
# [11, 13, 15, 16]
# [17]

For Iterator#slice_before added by (#7152:

ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]
ary.slice_before(&.even?).each { |x| puts x }
# output:
# [1, 3, 5]
# [8]
# [10, 11, 13, 15]
# [16, 17]

For Iterator#slice_when added by #7159:

ary = [1, 1, 1, 2, 2, 3, 4, 4]
ary.slice_when { |x, y| x != y }.each { |x| puts x }
# output:
# [1, 1, 1]
# [2, 2]
# [3]
# [4, 4]

Using ary.chunk_while { |x, y| x == y }.each { |x| puts x } would’ve lead to the same result.

Note that some of these methods have a reuse param to avoid unneeded allocations per slice/chunk. Mind the allocation!

Enumerable#one? has joined the party of #all? and #none?. It helps you check if a collection has only one truthy value. Read more at #7166.

Multiple methods in Enumerable, Iterator and Array can now accept a pattern. Patterns are things that implement #=== like Range, Regex, or any class. This will allow you to write things like:

[2, 3, 4].all?(1..5)        # => true
[2, 3, 4].all?(Int32)       # => true
[2, "a", 3].all?(String)    # => false
%w[foo bar baz].all?(/o|a/) # => true

This applies to methods like #select, #reject, #all?, #any?, #none? and the new ones described above. Read more at #7174.

Enumerable#to_h(&block) will now let you generate a hash from a collection in an easier way. Read more at #7150.

Serialization

Thanks to JSON::Any#to_yaml and YAML::Any#to_json converting between these formats is now a piece of cake. Read more at #7232.

Networking

The following feature will leave some of you speechless. There is now a MIME registry.

Multiple PRs like #5765, #7077 and others implement this feature described fully in their docs.

Essentially, there is an updated default list of known MIME types, plus the operating system’s MIME database is loaded if available and you can override these values.

The registry can be queried for extensions or filenames.

require "mime"

MIME.from_extension(".html")         # => "text/html; charset=utf-8"
MIME.from_filename("path/file.html") # => "text/html; charset=utf-8"

This is used in HTTP::StaticFileHandler but you can give it other uses for sure.

System

Every platform integration counts, even the smallest changeset. In #7041 the inlined ASM when compiling for ARM got a fix. And in #6972 methods to get hostname and cpu count in Win32 were implemented.

Compiler

There were many fixes related to improving error messages of the compiler in different scenarios. Don’t miss the full changelog. One of the most noticeable improvements is related to the type inference in recursions that involve blocks.

The following code was unable to compile as is in 0.27.0 but now it works like a charm.

class Tree
  def initialize(@value : Int32, @children : Array(Tree))
  end

  def sum : Int32
    @value + @children.each.map(&.sum).sum
  end
end

Tree.new(1, [] of Tree).sum

Read more at #7161.

Formatter

The formatter didn’t fall short on improvements. Besides handling more cases, and handling some of them better, #7257 has reworked the CLI to return more accurate status codes, behave more consistently and support opt-in error traces for people who want to get involved in the formatter itself.

Next steps

Please update your Crystal and report any issues. We will keep moving forward and start the development focusing in 0.28. In case you missed it, there are ways to test your shards and apps with nightly releases, it’s great to keep updated on upcoming breaking changes.

The development is possible thanks to the community’s effort, 84codes’ support, and every supporter.

Crystal 0.27.1 has been released!

This release includes new features and improvements in compiler errors that will be useful for many scenarios: scientific, web apps, cli, future advent of codes, you name it. There were 202 commits since 0.27.0 by 33 contributors.

This release ended up being bigger than expected, and we decided to close it at a point that made sense, and defer some WIP for the next version. There are a couple of breaking changes waiting to be merged already to start the process of 0.28.

Let’s review some of the most relevant changes in this release. But don’t miss the rest of the release changelog with a lot of valuable information.

Language changes

You already know tuples {1, "foo"}. We now allow a trailing comma as in {1, "foo", } to be a valid tuple. This comes in really handy when the tuple is generated from macros, for example. More on this at #7182.

Macros

There is a new macro that allows you to embed the content of a text file as a string literal during compile time. Neat for embedding text resources. This new macro comes in two flavors:

  • read_file(filename) that will raise a compile time error if the file does not exist.
  • read_file?(filename) that will generate a nil value if the file does not exist.

You can read about more about at #6967 and #7094 since there was some rethinking after the first PR and in the docs.

Numeric

The compiler is now able to emit overflow checks, but for now this feature is only available with an opt-in flag, to allow a migration path and feedback. In a future version, the overflow checks will be the default. When you build a program you can pass -D preview_overflow to enable them. Depending on your setup you might also need to use -D compiler_rt until we ship the compiler-rt dependency properly.

Assuming the overflow checks are turned on, methods like +, -, *, **, to_i8 and Int32.new will raise an OverflowError if the result doesn’t fit within the representable values. This applies to all integer types (including 128).

In case you need wrapping (a.k.a.: previous) behaviour you will need to use &+, &-, &*, &**, to_i8!, Int32.new!.

Many parts of the stdlib and the compiler had been updated to explicit use &+ (like Hasher) but if we happened to miss any, it would be great to hear about it. Check your apps with -D preview_overflow and let us know if you found any issues (before the overflow is the default behaviour).

You can read more about that at #7206 where you will also find other PRs related to it.

While implementing that, we also took care of some leftovers related to Int128 support like #7135

Also, one of those big little changes that bring joy to some of you, is that you are now able to use BigInt.new("1_000") to mimic 1_000. Read more at #7107.

Collections

There were many additions to Iterator, Enumerable that will come in handy in the next advent of code, and when processing collections of data.

Let’s see some quick examples of these additions. For Iterator#slice_after added by #7146:

ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]
ary.slice_after(&.even?).each { |x| puts x }
# output:
# [1, 3, 5, 8]
# [10]
# [11, 13, 15, 16]
# [17]

For Iterator#slice_before added by (#7152:

ary = [1, 3, 5, 8, 10, 11, 13, 15, 16, 17]
ary.slice_before(&.even?).each { |x| puts x }
# output:
# [1, 3, 5]
# [8]
# [10, 11, 13, 15]
# [16, 17]

For Iterator#slice_when added by #7159:

ary = [1, 1, 1, 2, 2, 3, 4, 4]
ary.slice_when { |x, y| x != y }.each { |x| puts x }
# output:
# [1, 1, 1]
# [2, 2]
# [3]
# [4, 4]

Using ary.chunk_while { |x, y| x == y }.each { |x| puts x } would’ve lead to the same result.

Note that some of these methods have a reuse param to avoid unneeded allocations per slice/chunk. Mind the allocation!

Enumerable#one? has joined the party of #all? and #none?. It helps you check if a collection has only one truthy value. Read more at #7166.

Multiple methods in Enumerable, Iterator and Array can now accept a pattern. Patterns are things that implement #=== like Range, Regex, or any class. This will allow you to write things like:

[2, 3, 4].all?(1..5)        # => true
[2, 3, 4].all?(Int32)       # => true
[2, "a", 3].all?(String)    # => false
%w[foo bar baz].all?(/o|a/) # => true

This applies to methods like #select, #reject, #all?, #any?, #none? and the new ones described above. Read more at #7174.

Enumerable#to_h(&block) will now let you generate a hash from a collection in an easier way. Read more at #7150.

Serialization

Thanks to JSON::Any#to_yaml and YAML::Any#to_json converting between these formats is now a piece of cake. Read more at #7232.

Networking

The following feature will leave some of you speechless. There is now a MIME registry.

Multiple PRs like #5765, #7077 and others implement this feature described fully in their docs.

Essentially, there is an updated default list of known MIME types, plus the operating system’s MIME database is loaded if available and you can override these values.

The registry can be queried for extensions or filenames.

require "mime"

MIME.from_extension(".html")         # => "text/html; charset=utf-8"
MIME.from_filename("path/file.html") # => "text/html; charset=utf-8"

This is used in HTTP::StaticFileHandler but you can give it other uses for sure.

System

Every platform integration counts, even the smallest changeset. In #7041 the inlined ASM when compiling for ARM got a fix. And in #6972 methods to get hostname and cpu count in Win32 were implemented.

Compiler

There were many fixes related to improving error messages of the compiler in different scenarios. Don’t miss the full changelog. One of the most noticeable improvements is related to the type inference in recursions that involve blocks.

The following code was unable to compile as is in 0.27.0 but now it works like a charm.

class Tree
  def initialize(@value : Int32, @children : Array(Tree))
  end

  def sum : Int32
    @value + @children.each.map(&.sum).sum
  end
end

Tree.new(1, [] of Tree).sum

Read more at #7161.

Formatter

The formatter didn’t fall short on improvements. Besides handling more cases, and handling some of them better, #7257 has reworked the CLI to return more accurate status codes, behave more consistently and support opt-in error traces for people who want to get involved in the formatter itself.

Next steps

Please update your Crystal and report any issues. We will keep moving forward and start the development focusing in 0.28. In case you missed it, there are ways to test your shards and apps with nightly releases, it’s great to keep updated on upcoming breaking changes.

The development is possible thanks to the community’s effort, 84codes’ support, and every supporter.