Crystal 0.34.0 released!
Crystal 0.34.0 has been released!
Get excited because this release comes with some of the final touches to the language before 1.0: a better exception hierarchy to replace Errno, a new logging module, cleanups and fixes aiming for a better, more stable release, one that should make us all proud.
There are 183 commits since 0.33.0 by 26 contributors.
Let’s get right into some of the highlights in this release. But don’t miss out on the rest of the release changelog which has a lot of valuable information.
Language changes
Exhaustive Case
From now on a case
expression will no longer have an implicit else nil
. This is useful to enable an exhaustive check across the when
branches within the case
. If you are doing case exp
over a union or an enum value, and you are missing a when
to cover some type or value, the compiler will let you know. Unless you have an else
at the end of the case
.
Note: In this version, when the case
does not cover all the possibilities, a warning is generated, and the else nil
is implicitly added. In the next version it will produce a compile-time error and the implicit else nil
will be gone.
The following snippet complains about the missing when Char
a = 1 || 'x' || "foo"
case a
when Int32
# ...
when String
# ...
end
And the following snippet complains about the missing when West
enum Direction
North; South; East; West
end
d = Direction::North
case d
when .north?, .south?
# ...
when .east?
# ...
end
The only case that will still have an implicit else nil
is when there is no expression and only a list of when
statements. This construction is equivalent to multiple if
/elseif
where there is an implicit else nil
also.
x = 1
y = 2
case
when x.even? # if x.even?
# ...
when y >= 10 # elsif y >= 0
# ...
end # end
Read more at #8424.
Procs subtyping
While dealing with Procs
and callbacks it is common to not use the return value. In Crystal, that usually means returning nil
. In regular methods you can specify the return type : Nil
to ignore the value of the last expression.
The counterpart in Procs
is harder because there usually is no type annotation for the return type.
For ease of use, we make it that any Proc(T)
should be able to be used as a Proc(Nil)
. That is, ignoring the return value in runtime. So, for those that like formality, Proc(T) < Proc(Nil)
is a valid subtyping rule now.
There was a previous attempt to achieve something similar, but in this version, a better handling of that affair was implemented. Read more at #8970.
Compiler
The disable_overflow
compiler flag is dropped. This means that the usual arithmetic operators will always have the overflow check. Use &+
and others to skip overflow checks. Read more at #8772.
The CRYSTAL_OPTS
environment variable can now be used to inject compiler options and flags implicitly. This is useful, for example, when the compiler is used in post_install
steps of shards and you want to enforce --error-on-warnings
. Read more at #8900.
LLVM 10 has just been released and we added support for it. Read more at #8940.
The codegen for Windows has been improved to work without --single-module
. Read more at #8978.
Shards
A new version of Shards (0.10.0) has been released. Until now you probably have been using Shards 0.8.1 which lacks some features. Shards 0.9.0 polished many use cases, but it uses a SAT solver, which doesn’t scale. For Shards 0.10.0 we created crystal-molinillo a port of the dependency resolution algorithm used by Bundler and CocoaPods.
You can read the rest of the updates in the release changelog.
We will be eagerly waiting for feedback from you on Shards to polish it before 1.0.
Standard library
Errno no more
Having as much as possible portable code is part of the goal of the std-lib. One of the areas that were in need of polishing was how Errno
and WinError
were handled. The Errno
and WinError
exceptions are now gone, and were replaced by a new hierarchy of exceptions. Unfortunately, there is no easy way to make a smooth transition here with deprecation warnings. The IO::Timeout
exception was renamed to IO::TimeoutError
to match the new hierarchy:
Exception
RuntimeError
IO::Error
IO::TimeoutError
(inheritsIO::Error
)File::Error
(inheritsIO::Error
)File::NotFoundError
File::AccessDeniedError
File::AlreadyExistsError
Socket::Error
(inheritsIO::Error
)Socket::ConnectError
Socket::BindError
So, you can now use these new types to catch specific errors instead of checking Errno
values. We included the most used errors as classes. If there is no specific class, the base File::Error
or Socket::Error
will be raised with a meaningful description.
The Errno
or WinError
underlying value is still present if you need it, via the SystemError
module included in this new hierarchy. But it is better if you avoid using it.
Read more at #8885.
Log
The former Logger
module is deprecated and will be removed soon. Its replacement is the Log
module: it’s shorter, more flexible and convenient.
You can use the top-level Log
constant to emit log entries, or you can declare one inside your module or class. This allows the entries to be emitted from a source.
Each source will be configured to send the entries to different backends depending on the severity level. If you initialize the logging with Log.setup_from_env
you will be able to filter the level and the sources using the CRYSTAL_LOG_LEVEL
and CRYSTAL_LOG_SOURCES
environment variables.
# file app.cr
require "log"
Log.setup_from_env
class MyApp
Log = ::Log.for(self)
def run
Log.debug { "the app is running" } # log from myapp source
end
end
MyApp.new.run
Log.info { "finished" } # log from the top-level source
If you want to log see all the log entries of the app above, you will need to set both environment variables, since their default values are CRYSTAL_LOG_LEVEL=INFO
CRYSTAL_LOG_SOURCES=""
(only top-level).
$ CRYSTAL_LOG_LEVEL=DEBUG CRYSTAL_LOG_SOURCES="*" ./app
D, [2020-03-30T21:54:50.079554000Z #26206] DEBUG -- app:my_app: the app is running
I, [2020-03-30T21:54:50.079624000Z #26206] INFO -- app:: finished
Read more at #8847 and check the docs for how you can define your own backends and use more advanced features of this module.
Top level cleanup
As we prepare for 1.0, we wanted to iterate and clean up some of the top-level of the std-lib and prelude. That is the reason behind many deprecations that involved part of Colorize
in #8892, Iconv
in #8890, DL
#8882.
Some modules were moved out of the top-level: Adler32
and CRC32
are inside Digest
#8881, and AtExitHandlers
inside Crystal
#8883.
There might be some more cleanups/renames before 1.0 to avoid wanting some trivial early breaking-changes.
Collections
On the performance corner of this release, when using Array#fill
for writing all zero values, it will now use memset
for the entire underlying buffer, instead of iterating every position. Read more at #8903.
Serialization
There is a small breaking change in YAML
in order to align the API of all builders. YAML::Builder.new
with block was renamed to YAML::Builder.build
in #8896.
When using the different format builders, IO#flush
will be called to ensure all the content will get through in case you are not closing properly the destination file. This applies to CSV
, INI
, JSON
, XML
, and YAML
builders. Read more at #8876.
Time
It’s time for more breaking-changes in favor of less error-prone code. The Time::Span
initialization API will use mandatory named arguments, like Time::Span.new minutes: 2, seconds: 3
. Read more at #8257.
Files
When closing a File
or Socket
the internal fd
is set to -1
to force an invalid file descriptor and avoid mixing fd
from different IO
s. In single-thread, this was never an issue, but on multi-thread, as usual, issues like this one can cause big headaches. Read more at #8873.
The IO::Buffered#flush_on_newline
is back. And its default value will be helpful for building CLI tools and pipe them into other commands. Read more at #8935.
HTTP
The WebSocket support was lacking the proper handling of close code. In order to implement them, a breaking-change on the server-side and in client-side parts was needed. Read more at #8975 and #8981.
Windows
The windows support is moving forward while enabling more specs, and more contributors are jumping into the adventure. Check out #8683 and #8822, #8885, #8958.
Tools
The crystal init
tool got some polishing. The name of the shard is validated with respect shards spec and it can be inferred from the directory. Read more at #8737.
The crystal docs
tool will now show warnings. In previous releases we switched to :ditto:
and :nodoc:
as magic comments. But we missed showing you the warnings in case you forget to add the colons. Read more at #8880.
Next steps
Please update your Crystal and report any issues. We will keep moving forward and start the development focusing on 0.35. There won’t be many more 0.x releases. We are getting super close to 1.0!.
Again, we will be eagerly waiting for feedback from you on Shards to polish it before 1.0.
All deprecation warnings will soon be gone, and there will be errors in the next release. We want a clean 1.0.
We have been able to do all of this thanks to the continued support of 84codes, Nikola Motor Company and every other sponsor. It is extremely important for us to sustain the support through donations, so that we can maintain this development pace. OpenCollective and Bountysource are two available channels 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