Crystal 0.16.0 has been released!
This is a huge release that includes a major breaking change that was announced a few months ago: types of global, class and instance variables need to be a bit more explicit.
This release also includes other minor breaking changes and a lot of new goodies.
The new global type inference algorithm
The new rules are explained in the official docs, but let’s quickly review the change.
Before this release, the type of global, class and instance variables was inferred globally in the program by analyzing all uses. For example:
In the above snippet,
@var was inferred to be an
Int32. If you did this:
then it would have been inferred to be
Int32 | String (a union type). And even
in the following code,
@var was inferred to be an
Int32 | String:
In this release, all of the above snippets won’t compile anymore: the compiler now
needs to know the type of
@var in a “obvious” way. For example, assuming the
intended type for
Int32, then we could write:
Another common ways is using literals and constructors:
The reason of this change is to allow, in the future, implementing incremental compilation and improving overall compile times and memory usage. Right now there aren’t many big projects written in Crystal. Probably the biggest one is the compiler itself, and it takes 16 seconds to compile it from scratch, and 1GB of memory. But bigger projects will exist, and even though a programmer’s computer should be fast and have a lot of memory, that’s no reason to have her wait, or waste CPU and memory. Yes, there are popular programming languages that can sometimes reach huge compile times, but that’s no excuse for us to do the same.
For the old global type inference to work, the whole code had to be held in memory, as a big tangled web, because a change in the type of an instance variable could impact any other method. With this change, methods can be analyzed locally. And once they are, their type can be inferred and it can’t change anymore.
Note that types in method arguments are not mandatory, and will never be.
The good side of this change is that since the types of instance variables must now be obvious to the compiler, they will also be obvious for someone reading the code. The programmer, too, has to stop analyzing the whole code to figure out what an instance variable is supposed to be.
Our guess is that static type languages lovers will love this change, while more dynamic type languages lovers will probably hate it, some a bit, others with fury.
The good news is that even after this change explicit types are still not that many. As an example, these are some diffs that were needed in some projects to upgrade to the new version:
- - crystal-toml: 2 types annotations
- - webmock: 3 type annotations
- - crystal_slack: 9 type annotations
- - crystal_brium: 3 type annotations
- - crystal-sqlite3: 3 type annotations
- - crystal_lib: 55 type annotations
- - shards: 32 type annotations
- - radix: 3 type annotations, plus making some types generic
- - minitest: 13 type annotations
- - artanis: 1 type annotation
- - msgpack: 5 type annotations
In general, few type annotations were needed. That sometimes depends on the programmer’s style: he might feel more comfortable with more explicit types, so this change affects him less. In other cases more annotations are needed, but understand that these projects have been around for a long time now, and adding 30 type annotations at once instead of writing them when declaring a class is definitely more annoying.
Another reason for why not many type annotations were needed is that many were already there, since the language was born:
Empty arrays and hashes always needed their type specified, and these are very common when initializing an object.
As can be seen above, many important Crystal shards have already been updated and will work with this release.
If you haven’t upgraded yet, the recommended approach is to ask the old compiler (0.15.0) these types,
crystal tool hierarchy your_program.cr and then adding the necessary type annotations that
the new compiler (0.16.0) will ask. To have both versions side by side you can use the excellent
crenv by pine.
Putting the big breaking change aside, this release includes many goodies.
FreeBSD and musl libc support
His contribution will also make it easier to port Crystal to other platforms (but, before you ask it in the comments section, no, there’s still no Windows support, and this change probably doesn’t help much in that regard.)
EDIT: a FreeBSD package is now in the releases page.
Named arguments everywhere
Before this release, named arguments could only target arguments that had a default value:
Now, all of the above compile. This can be specially useful for methods that have a long list of arguments. For example, which one is more readable:
Regardless of which option you find more readable, the first one is actually wrong:
the method arguments are
(host, client_id, client_secret), and they are
being passed in a wrong order. But, because all of them are strings, the compiler
doesn’t complain. The second option is more robust because we don’t need to
remember the correct order and we use descriptive names.
More big numbers
Binary search methods were added in
Range (thanks to MakeNowJust).
For example, let’s solve x3 + x2 + x - 2:
JSON and YAML improvements
BigFloat can now be mapped to JSON and YAML very easily. For example:
Make sure to read the changelog for other minor goodies (and a few minor breaking changes as well.)
We’d like to thank everyone that made this release possible, by testing the new changes and reporting bugs, upgrading code to the latest version, sending pull requests, commenting suggested features, adding docs and more.