Skip to content
GitHub Repository Forum RSS-Newsfeed

Crystal 0.25.0 released!

Brian J. Cardiff

Crystal 0.25.0 has been released!

As every release, it includes numerous bugfixes, cool features and performance improvements - in 400 commits since 0.24.2 from 47 contributors. There needs to be a special mention to @MakeNowJust, @straight-shoota, @Sija and @bew for their hard work in this release.

There were a ton of contributions merged in master even before 0.24.2 was released. But since 0.24.2 was already changing the release packaging for linux, changing the CI and fixing 0.24.1, some features needed to wait their turn a little longer.

Once again, we have tested this release by compiling some of the most popular crystal shards. This helps us catch and fix unintended breaking changes earlier in the release cycle as well as submitting PRs to the shards and contributing a bit more with the community. This process is codified using the scripts in the test-ecosystem repository, which is still fairly new, but so far it’s working well.

The least visible work usually goes in infrastructure and there are always improvements and things waiting to be done. The latest news regarding this area are:

  • Docs in master are back. For every PR that is merged the docs at HEAD can be found at /api/master/.
  • Improved SEO by adding a canonical url for online docs #5990.
  • Also on docs, lots of improvements regarding navigation have been done in #5229.
  • The automated release process now cares about 32 bits linux releases. As a bonus point the packaging has been aligned again with respect to the 64 bits packages. So some paths have changed.
  • We’ve been contacted by Heroku to early register our buildpack. Stay tuned to future Heroku news to update to the crystal-lang/crystal buildpack in the registry. All in all it’s one more taste of the adoption of Crystal out there, and we are thrilled.

Nightly packages in are still down. The workaround for now it to use the docker image crystallang/crystal:nightly.

Exciting features

Shards is updated to 0.8.0

There are some performance improvements in shards for this release, by downloading less information when possible. A new global cache was added, so you don’t need to download your favorite shards over and over on all of your favorites projects. FYI you can use shards 0.8.0 with Crystal 0.24.2 if you want.

Read more here.

Automatic casts for literal values

If a method is defined as def foo(x : Int8) or def bar(color : Color) with

enum Color

up to 0.24 you would need to call them as foo(1i8) or bar(Color::Blue). But since 0.25.0 you will be able to foo(1) and bar(:blue). A note of caution: this only work with literal values. If the value is saved in a variable and used as an argument it won’t work.

This feature allows cleaner code without sacrificing safety. Read more at #6074.

User defined annotations and [JSON|YAML]::Serializable

This new language construct allows the user to define their own annotations, like [Link]. Basically you will be able to annotate types declaration or instance variables, and later on query them to do something you wish in macros.

Before this feature metaprogramming usually involved calling one macro with all the information needed. From now on, a more decoupled mechanism between declaring and consuming can be used. Read more at #6063.

The new JSON::Serializable and YAML::Serializable modules use this annotations. Feedback is welcome since this feature is brand new. You can read more at #6082, JSON::Serializable, YAML::Serializable docs.

Another usage of annotations might be to declare a registry of classes, like the one used in DB drivers or frameworks handlers. And it could enable the removal of mutating values of constants during compilation time in favor of a more declarative code.

Do not collapse unions for sibling types

Code is worth a thousand words (you know, like pictures):

class Foo

class Bar < Foo

class Baz < Foo

var = rand < 0.5 ? :
typeof(var) #=> Bar | Baz

Up to 0.24.2 the result was typeof(var) #=> Foo.

Although the previous code already compiled fine in 0.24.2 this changes allow the type system to deal with some cases that would have ended in a compile-time error but that actually make sense. At the end of the day the type system is about identifying which programs will safely run and cutting the ones that won’t.

The following program is an example of that. It won’t compile in 0.24.2 but it now does in 0.25.0.

class Foo

class Bar < Foo
  def do_it

class Baz < Foo
  def do_it

class Qux < Foo
  # there is no do_it

var = rand < 0.5 ? :

This is particularly useful in scenarios where there is a huge hierarchy of types but in a section of the code only a subset is used.

You can read more at #6024 and discover when the union of types are still collapsed to the common ancestor (spoiler, they need to not be siblings).

JSON::Any and YAML::Any changes

There were some subtle inconsistencies with JSON::Any and YAML::Any API. The bottom line is that over an ::Any value you can use #[] to traverse it and it will always return an ::Any value. If you need a specific type for the ::Any value (and be able to use Enumerable methods if it was an array) you need to call the already known #as_a, #as_h, #as_s methods.

We still encourage, when possible, the use of JSON.mapping, JSON::Serializable or JSON::PullParser when finer control is needed.

Read more at #5183 and in the JSON::Any and YAML::Any docs.

HTTP::Server can bind to multiple addresses

This will break lots of presentations and even the code shown in our own homepage but the benefits are great.

From now on if you use the built-in HTTP::Server you first need to configure it, then bind to one or more addresses, and lastly you listen to all of them. These addresses can be TCP ports or Unix sockets.

require "http/server"

server = do |context|
  context.response.content_type = "text/plain"
  context.response.print "Hello world, got #{context.request.path}!"

server.bind_tcp "", 8080
server.bind_unix "/tmp/app.sock"

There is still a shortcut to bind and listen, but it doesn’t avoid a breaking change. Read more at #5776, #5959, and the HTTP::Server docs

Welcome to the TimeZone Jungle

There was a huge refactor in Time. If you hit a unicorn while opening the PR to read more about it, just try again.

Starting now Time has #location and #offset properties to know the timezone exactly. and will return by default information in the local timezone, while Time.utc_now and Time.utc will return information in UTC.

Methods like #to_local, #to_utc, #utc?, #local? and #in(location : Location) will help you to move around the globe faster than a plane.

The API even allows you to use custom timezones and fixed offsets with Time::Location.fixed.

Another change in the Time namespace are formatters. Better formatters for ISO 8601, RFC 3339, RFC 2822, HTTP enconded dates, YAML and other places where time was parsed or emitted now use a custom time formatter that deals with more cases as expected in each scenario.

Read more at #5324 and #5123 and Time, and Format docs.

Replace File::Stat with File::Info and other file API changes

Some time ago an abstraction for the running OS was introduced in the stdlib. The goal was to be able to run the Crystal compiler in a non POSIX platform and keep the stdlib as clean as possible. Feel free to check src/crystal/system, but keep in mind it is not intended as a public API.

This also required to pick names and abstractions in the stdlib that will fit everybody: POSIX and non POSIX.

The API was renamed and reworked for compare operations and accessing file properties and permissions. It is much clearer now. Hopefully it doesn’t affect too many users, since most of us use, File.write and move on. Read more at #5584, #5333, #5553, #6161, File and File::Info docs.

Heredoc on every argument

If you use Heredoc a lot of you might be interested in this one. Up to 0.24.2 if you wanted to call a method on a string specified using Heredoc you would do:

puts <<-FOO

From now on the method needs to be at the initial delimiter

puts <<-FOO.downcase

It’s subtle but important, and it plays better with multiple Heredocs in a single call now that you can:

puts <<-ONE.upcase, <<-TWO.capitalize
  hello world
  second message

Read more at #5578.

Macro verbatim blocks

If you deal with escaped macros don’t miss #6108.

Macros are powerful and they should be used after there is a boilerplate pattern discovered.

This new language construct helps when the macro itself will define, for example, methods that have macro blocks that should be expanded later (i.e. nested macros).

It may result in a nicer way to express the same things you could before with some \{% escaping %}.

Other notable changes (breaking or not)

  • crystal deps is dead, long live shards install. #5544. Unless we removed it, you would never have updated your build scripts.
  • Use Hash#key_for to perform a reverse lookup in a hash #5444 #NamesAreHard.
  • The block argument from loop was removed #6026.
  • Fix File.join with empty path component #5915.
  • Colorize#push is dead, long live Colorize#surround#4196. Bonus point, your #to_s can use your favorite color now.
  • Punycode is a special encoding used to convert Unicode characters to ASCII and is used to encode internationalized domain names (IDN). And now they are available in Crystal thanks to #2543.
  • pp no longer prints the expression. But pp! and the new p! will. p stands for print, pp for pretty print and ! for show me the money expression #6044.

Next step

Please update your Crystal and report any issues. If there are regression or blocking issues with 0.25.0, a 0.25.1 could be released earlier.

Don’t miss the rest of the release changelog information with lots of other fixes.


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