Crystal 1.5.0 is released!
We are delivering a new release with several bugfixes and improvements. Below we list the most important or interesting changes, without mentioning several bugfixes and smaller enhancements. For more details, visit the changelog. Breaking changes are marked with ⚠️.
Pre-built packages are available on GitHub Releases and our official distribution channels. See crystal-lang.org/install for installation instructions.
Stats
In this release we included 102 changes since the 1.4.1 release by 23 contributors. We thank all the effort put into improving the language! ❤️
Below we list the most remarkable changes in the language and stdlib.
⚠️ Parameters of methods implementing an abstract def
must match the names
In order to provide better documentation and robustness, it is possible to explicitly associate arguments with their names (ref):
class Foo
def foo(name : Int32) : Nil
p name
end
end
Foo.new.foo name: 42
As a consequence, it is sensible to consider that the name of an argument is part of its interface. Before 1.5.0, however, the compiler was not checking that the name of arguments matched between an implementation of an abstract method and its definition. That is, the following example compiled without error or warning:
abstract class FooAbstract
abstract def foo(number : Int32) : Nil
end
class Foo < FooAbstract
def foo(name : Int32) : Nil
p name
end
end
Starting from 1.5.0 (#11915) the example above will raise a warning:
6 | def foo(name : Int32) : Nil
^---
Warning: positional parameter 'name' corresponds to parameter 'number' of the overridden method FooAbstract#foo(number : Int32), which has a different name and may affect named argument passing
Method restrictions from instance variables
When an instance variable is assigned the value of an untyped method argument, then the argument is restricted to share the same type as the instance variable.
For instance, consider this code:
class Foo
@x : Int64
def initialize(x)
@x = x
end
end
Up to 1.4.1, x
in initialize
is unrestricted. This had several issues:
-
If the user passes an incorrect argument, like
Foo.new 'a'
, instead of marking the error in the argument'a'
, it blamesx
for not having the right type. -
No autocast is performed, for instance, if we pass an
Int32
instead:Foo.new 1
fails. -
The generated documentation doesn’t provide a hint of the type of the parameter
x
.
From 1.5.0, in an assignment like @x = x
, parameter x
gets the type of @x
, effectively solving the three issues above. Details can be read from #12103.
Note: This new feature is expected to be backwards compatible. If for some reason it brings you a headache you can disable it passing
-Dno_restrictions_augmenter
in the build options. Let us know in the issue tracker about it.
Annotations allowed on method arguments
It is now possible to add an annotation to a parameter of a method or macro. As illustration, imagine a linter that warns if a parameter is not used.
def foo(x); end # Warning: argument `x` is not used
Then, we could signal the linter to not warn us in a particular case. Assume the following annotation provided by the linter:
annotation MaybeUnused; end
Applying it to the parameter removes the warning (in this particular fictitious linter):
def foo(@[MaybeUnused] x); end # OK
Details in #12039.
Constant indexers for tuples
When using a constant to index a tuple or named tuple, the typechecker will correctly infer the precise type of the value accessed (#12012).
KEY = "s"
foo = {s: "String", n: 0}
# Before 1.5.0 this failed; it would assume the type of foo[key] to be (String | Int32)
puts foo[KEY].size
Additions in File API
New method File#delete?
that, instead of raising, returns false
if the file doesn’t exist. Similarly, there’s a new Dir#delete?
. Details in #11887.
Additionally, several File
methods now also operate on an instance: File#chmod
, File#chown
, File#utime
, and File#touch
. Details in #11886.
Strengthening the security of File.tempfile
As per #12076, the creation of temporary files don’t allow null characters in the strings forming the name of the file.
NO_COLOR compliance
The compiler and the interpreter supports the NO_COLOR environment variable to disable colored output on the terminal. This is enabled by setting any non-empty value to NO_COLOR
. Details in #11984.
A big step towards native Windows support
The concurrency runtime on Windows is now backed by a functioning event loop (#12149).
This crossed an important check in the road to native Windows support. Additionally, we now have a Windows-compatible Makefile
(#11773).
Improvements in the interpreter
The interpreter is coming to shape! A whole bunch of interpreter bugs got fixed.
While it won’t be released in the binaries distributions yet, we will work towards incorporating it in the major platforms right after this release. Stay tuned!
You can build the compiler with interpreter support locally with make interpreter=1
, see the interpreter introduction post for more info.
We have been able to do all of this thanks to the continued support of 84codes, Nikola Motor Company and every other sponsor. To maintain and increase the development pace, donations and sponsorships are essential. OpenCollective is available for that.
Reach out to crystal@manas.tech if you’d like to become a direct sponsor or find other ways to support Crystal. We thank you in advance!
Contribute