Crystal 0.36.0 has been released!
Since 0.35.1 there has been lots and lots of polishing, new features, and important fixes. This created a bigger delta than we wanted to transition from the last 0.x release to the first 1.0-preX. As a result, we are releasing a 0.36.0. This should help the community migrate their packages with less friction to the changes that will appear in 1.0. It will also give us a chance to get rid of a couple of recently introduced deprecations.
Note that this release will be available in the new apt and rpm repositories post, as well as GitHub release artifacts. You might need to update your package repositories.
There were 346 commits since 0.35.1 by 54 contributors.
Let’s get right into some highlights in this release. Don’t miss out on the rest of the release changelog.
Although instance variables can have annotations, we must apply them in the base class that declares them. So we reject annotations on instance variables redefined in a child class. We might lift this restriction in the future, but we are going to play it safe first. Read more at #9502.
** operator is right-associative from now on. This matches Ruby and a couple of other programming languages. So
2 ** 2 ** 3 == 2 ** (2 ** 3) == 256. But note that the negation is done before the exponentiation because of how we parse negative number literals. So
-2 ** 2 == (-2) ** 2 == 4, which is different to Ruby. Read more at #9684.
Some releases ago
TypeNode#annotation was added. The former less powerful
TypeNode#has_attribute? is deprecated now. Read more at #9950.
There are several fixes that, although formally lead to breaking-changes, are mostly edge cases that required some polishing.
- You can’t use keywords as block arguments names. #9704
a-b -cexpression is now correctly parsed as
(a - b) - c. #9652, #9884.
When using abstract
defs, the compiler will enforce a couple of additional rules that should improve the maintenance of the code base.
defimplementations must honor abstract declaration regarding type restrictions, splats, default values, and keyword arguments. #9585, #9634, #9633.
defimplementations must honor return type abstract declaration. #9810
A simple example of what this enforce would be:
abstract class Foo abstract def m(x = 1) end class Bar < Foo def m(x) # Error: because Foo#m can be called with no argument. # Declare it as def m(x = 1) to make the compiler happy. end end
Regarding typing, we improved a couple of stories.
- The type variables unification is more correct regarding union types. (#10267, thanks @HertzDevil)
- Exhaustive case expressions types to non-nilable value if all the cases allow it. #9659
- Auto-casting deals better when the type restriction is a union by using, if possible, the exact type of the literal argument. #9610
- Typing rules involving closured variables got smarter. #9986
- Typing rules involving type restrictions, unions and boolean operators are really smarter. #10147.
How smarter you would like to know. Here is a sneak peak of what is now possible but it wasn’t before.
x = 1 || 1.0 || 'a' || "" # x : Int32 | Float64 | Char | String # when-clauses of >= 3 types now work, including else-branches of the case statement case x when Int32, Float64, Char typeof(x) # => Int32 | Float64 | Char else typeof(x) # => String end # negations of disjunctions now work if !(x.is_a?(Int32) || x.is_a?(Float64)) typeof(x) # => Char | String else typeof(x) # => Int32 | Float64 end # the de Morgan equivalent of the above also works now if !x.is_a?(Int32) && !x.is_a?(Float64) typeof(x) # => Char | String else typeof(x) # => Int32 | Float64 end
You can also declare
defs overloads with different named tuple types. Really handy if you are into using tuples a lot. Read more at #10245.
A breaking-change that affects C bindings is introduced in #10254. Callbacks within
lib should be declared now as
alias instead of
type. One less quirk in the compiler.
lib YourLib --- type ACallback = Int32 -> Int32 +++ alias ACallback = Int32 -> Int32 end
A second breaking-change that affects C bindings is that a Crystal nil value is no longer translated to a null pointer value. Read more at #9872.
The following top-level deprecated definitions are dropped:
with_color. You will need to migrate to the
Digest namespace. Read more at #9530, #9529, and #9531.
SemanticVersion obeys the standard (as it should). This causes some breaking-changes if you rely on invalid version names, but otherwise you are safe. Read more at #9868.
Unfortunately some breaking-changes are silent. There is no simple way to show a warning. Such a case is when the return type of a method is changed. This is what happens to
FileInfo#size since they now return Int64 instead of UInt64. Read more at #10015.
If you use
Complex, you will need to rewrite some operations like
Complex#log, etc. to
Math.log. This is the same API as for other numerical types. Read more at #9739.
String can hold arbitrary byte sequences. When these turned out to be invalid UTF-8 sequences, operations like
Char::Reader were misbehaving. In the presence of an invalid UTF-8 sequence, the data is now iterated a bit more slowly, like 1 byte at a time, until a valid sequence is found. Read more at #9713.
There is a set of changes seeking consistency over the collection types. Unfortunately some are silent breaking changes.
Array. Bonus point: you can method chain. #9904.
Boolto indicate if the element was present. #9590.
Hash#key_indexwas removed. #10016
But not every change is a removal, the following additions are also part of the release!
Iterator#zipto take multiple
Array#unshiftgot faster. #10081
We try to have one way to do things. This comes at the expense of needing to migrate old solutions that served us well.
YAML.mapping are no longer part of the std-lib since there is a more flexible alternative:
YAML::Serializable. You can either migrate or, if you still want them, you can use github:crystal-lang/json_mapping.cr and github:crystal-lang/yaml_mapping.cr. Read more at #9527 and #9526.
You can now declare properties that will be used only on serialization or deserialization. If your model has a field that should be hashed for serialization this comes in really handy. Read more at #9567.
Another addition is that you can use
use_yaml_discriminator with other types than
String. For example, you can use numbers or enums to map the corresponding type. Read more at #9222 and #10149.
It’s time to mention some small breaking-changes:
Time::Span.newdeprecated variants are no longer available. (#10051
Time::Span#durationis deprecated in favor of
Time::Spanis already a duration, right? #10144
Some breaking-changes are almost bug-fixes, but since they mean changing a known behavior, it is worth mentioning them as such. After #10180,
FileUtils.cp_r will behave as expected when destination is a directory: the destination is calculated with
HTTP::Params is renamed to
URI::Params. In this release there is a deprecated alias that will allow you to deal with this migration more gradually. But I would not count on this alias to remain for long. Read more at #10098.
Another breaking-change is the renaming of
URI#request_target. Some edge-case behaviors also change to match the expected definition. Read more at #10099.
HTTP::Params), as you know, is used to represent query strings that can hold multiple values for a key. When assigning a new value to a key you will override all values of that key. Read more at #9605
params = URI::Params.parse("color=red&color=blue&console=gameboy") params["color"] = "green" params # => => "color=green&console=gameboy"
HTTP::Client can now be used with any
IO instead of
TCPSocket. If you happen to have an application that uses HTTP as protocol over UNIX Sockets, like Docker, you can now talk to it. Read more at #9543
require "http" io = UNIXSocket.new("/var/run/docker.sock") client = HTTP::Client.new(io, "localhost") response = client.get "/v1.40/images/json"
To have only one module to log them all, the former
Logger is no longer in the std-lib. Migrate to
Log or, for just a little longer, you can keep using github:crystal-lang/logger.cr. Read more at #9525.
If you need more excuses to migrate to
Log::Backend can now emit their entries with different strategies that should help keeping the throughput of the app in the presence of logging. Read more at #9432.
Time for some security alerts. The std-lib prevents by default the Secure Client-Initiated Renegotiation vulnerability attack. In #9815 you can find more info and a small patch if you need to secure existing apps. Also, if you use
verify_mode=force-peer, it is now correctly set up. Read more at #9668.
Digest module is now backed by
OpenSSL. Most used algorithms have their own class that is easy to use:
Digest#update(IO) are available to compute digest from file and
IO content. Read more at #9864.
We hid some
Channel API that should have been internal since the beginning. I doubt this will cause any issues. Read more #9564.
Channel#close now returns
true unless the channel was already closed. This is handy in multi-thread, because you might need to know if the current fiber is the one that actually closed the channel. Read more at #9443.
Process.parse_arguments will allow you to parse a
String in the same way a POSIX shell does. This is internally used to improve
CRYSTAL_OPTS parsing. Read more at #9518.
#should_not methods can now take an additional argument for custom failure messages. Read more at #10127.
describe "something" do it "something" do true.should eq(false), "Oh no!" end end
The doc generator got many improvements. In case you weren’t aware, besides generating HTML, it can also export the documentation information as JSON. This allows for the use of other documentation generation tools. With some of the recent additions listed in the changelog you can use mkdocs via some community development.
Please update your Crystal and report any issues. We will keep moving forward and focusing on releasing 1.0.0-pre1 which should be a more stabilized version of 0.36 without many new additions.
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 is available for that. Reach out to firstname.lastname@example.org if you’d like to become a direct sponsor or find other ways to support Crystal. We thank you in advance!