Crystal 1.18.0 is released!
We are announcing a new Crystal release 1.18.0 with several new features and bug fixes.
Pre-built packages are available on GitHub Releases and our official distribution channels. See crystal-lang.org/install for installation instructions.
Stats
Section titled StatsThis release includes 172 changes since 1.17.1 by 31 contributors. We thank all the contributors for all the effort put into improving the language! ❤️
Changes
Section titled ChangesBelow we list the most remarkable changes in the language, compiler and stdlib. For more details, visit the full changelog.
We do not expect any breaking changes in existing code. If you notice any unexpected issues, please let us know in the issue tracker or forum.
Execution contexts
Section titled Execution contextsExecution contexts from RFC 0002 continue as a preview feature with opt-in
with compiler flags -Dpreview_mt -Dexecution_context
. It might move out of
preview in the next release.
The default execution context is now parallel, but with maximum: 1
(#16136).
So it starts with a single thread, but it can grow to more with
Fiber::ExecutionContext::Parallel#resize
(#15956).
There are also a number of performance improvements for schedulers and event
loops (#15961, #16063) and we dropped the custom implementation of
Fiber::ExecutionContext::Concurrent
(#16135). It’s now based on the parallel
implementation.
Fiber::ExecutionContext.default_workers_count
has been improved (#16149) to
respect the effective CPU count available to the process (#16148).
The preview of synchronization primitives in ysbaddaden/sync
has seen some
improvements as well.
This effort is part of the ongoing project to improve multi-threading support with the help of 84codes.
Thanks, @ysbaddaden
Deprecation warnings
Section titled Deprecation warningsDeprecation warnings are now available on types and aliases (#15962) as well as individual method parameters (#15999). Deprecated types only trigger warnings when they are actually used (e.g. calling a class method), not when they’re just part of a type restriction, for example. Deprecated parameters only trigger a warning when the particular parameter is used in a call. Calls without this parameter are unaffected.
@[Deprecated("Here may be dragons")]
class Foo
end
def foo(bar, @[Deprecated("Do not try this at home")] baz)
end
Thanks @ysbaddaden
Time
Section titled TimeTime#inspect
Section titled Time#inspect
The format of Time#inspect
has been adjusted slightly to align with the Internet
Extended Date/Time Format (IXDTF) defined in RFC 9557 (#16039).
Changes from the previous format:
- Replace
UTC
location byZ
offset - Skip zero nanoseconds entirely even when
with_nanoseconds
is true (see https://github.com/crystal-lang/crystal/pull/16039#discussion_r2250863021) - Remove whitespace between time and offset
- Wrap location in square brackets to indicate an IXDTF time-zone suffix
Some examples show the differences:
a = Time.utc(2014, 1, 2, 3, 4, 5, nanosecond: 123_456_789)
b = Time.local(2014, 1, 2, 3, 4, 5, nanosecond: 123_456_789, location: Time::Location.load("Europe/Berlin"))
c = Time.local(2014, 1, 2, 3, 4, 5, nanosecond: 123_456_789, location: Time::Location.fixed(3600))
# Crystal 1.17:
a.inspect # => "2014-01-02 03:04:05.123456789 UTC"
b.inspect # => "2014-01-02 03:04:05.123456789 +01:00 Europe/Berlin"
c.inspect # => "-2014-01-02 03:04:05.123456789 +01:00"
# Crystal 1.18:
a.inspect # => "2014-01-02 03:04:05.123456789Z"
b.inspect # => "2014-01-02 03:04:05.123456789+01:00[Europe/Berlin]"
c.inspect # => "+2014-01-02 03:04:05.123456789+01:00"
Time::Location
Section titled Time::Location
Time::Location.local
now follows symlink names, so that it resolves to the same
timezone by name as Time::Location.load
, even if /etc/localtime
points to a
zoneinfo database (#16002, #16022).
Time::Location.load?
is a new, non-raising variant to .load
(#16121).
Thanks, @straight-shoota
The local Windows system time zone now uses the canonical IANA name (#15967).
Thanks, @HertzDevil
Serialization
Section titled SerializationYAML::Any
now resolves YAML aliases (#15941).
Thanks, @willhbr
The output of JSON::Any#inspect
got a wrapper to indicate
the Any
type and differentiate from the wrapped type (#15979).
The equivalent for YAML::Any
is still pending.
require "json"
JSON::Any.new(1).inspect # => "JSON::Any(1)"
Thanks, @jneen
Time::Location
can now be used as a JSON object key (#15957).
Thanks, @Sija
The private constructors of *::Serializable
moved into the macro included
hook (#16147). They’re now defined on the including types which is more robust
in terms of #initialize
overload ordering. This also allows referring generic
type variables inside converters.
Thanks, @HertzDevil
Standard library
Section titled Standard libraryNew methods:
Set#select!
and#reject!
(#16060)File.readlink?
(#16004)SemanticVersion.valid?
&SemanticVersion.parse?
(#15051)Socket.set_blocking
andIO::FileDescriptor#set_blocking
(#16033, #16129)OptionParser#summary_width
andOptionParser#summary_indent
(#15326)
Thanks, @HertzDevil, @straight-shoota, @devnote-dev, @ysbaddaden, @kojix2
Colorize
now uses scoped ANSI reset codes which only reset the respective
property, instead of resetting everything (#16052).
Thanks, @Blacksmoke16
Networking
Section titled NetworkingStaticFileHandler
preserves query params in redirects (#15789)
Thanks, @syeopite
StaticFileHandler
returns 404 on file error (#16025, #16077)
Thanks, @straight-shoota
HTTP::Client
runs the #before_request
callback only once, even if a
request is retried (#16064).
Thanks @straight-shoota
URI#host=
wraps IPv6 addresses in brackets (#16164)
Thanks, @stakach
Type restrictions
Section titled Type restrictionsWe added type restrictions to many API methods. This improves the documentation. This is a semi-automatic effort, assisted by a tool that automatically extracts typing information from the semantic analysis of a program (#15682).
MIME
(#15834), Log
(#15777), OAuth
(#15687), JSON
(#15840,
#16142), Benchmark
(#15688), Crypto
(#15694), IO
(#15698), HTTP
(#15710), Big*
(#15689), Process
(#16031).
Thanks, @Vici37
Code Cleanup
Section titled Code CleanupMulti-line strings containing source code in compiler specs have had two different formats: Some of them were using regular string literals, others used heredocs. The latter was usually preferred in new code additions because line numbers and indents are more sensible. And with an appropriate heredoc identifier to denote the language, we even get nested syntax highlighting. In this release we’ve converted all compiler specs to use heredocs (#11291).
Thanks, @HertzDevil
With the help of ameba
, we’ve enabled and applied a couple more linter rules:
Style/UnlessElse
(#16010), Style/RedundantBegin
(#16011), and
Lint/UselessAssign
(#16014).
Thanks, @straight-shoota
Macros
Section titled MacrosNew methods:
Empty NamedTupleLiteral
and TupleLiteral
expand to NamedTuple.new
and
Tuple.new
, instead of {}
(#16108).
Thanks, @spuun
Compiler
Section titled CompilerThe compiler can dump type information to a JSON file when the environment
variable CRYSTAL_DUMP_TYPE_INFO
is set (#16027).
Thanks, @HertzDevil
Resolve types when guessing return type from class method overloads (#16118).
In the following snippet, the compiler is able to infer @foo
’s type to be Foo
, since both overloads of Foo.foo
have a return type of Foo
:
class Foo
def self.foo(x : Int32) : Foo
new
end
def self.foo(x : String) : Foo
new
end
end
class Bar
def initialize
@foo = Foo.foo(1)
end
end
Bar.new
Thanks, @HertzDevil
Guess instance variable types from global method calls (#16119).
This allows the following to compile:
def foo(x : Int32) : Float64
# ...
end
class Bar
def initialize
@foo = ::foo(1) # okay, `@foo` has the type `Float64`
end
end
The call needs to be global; other than that, type guessing for top-level methods has the same limitations as for class methods.
Thanks, @HertzDevil
Hash literals are evaluated from left to right, fixing a regression from 1.6.0 (#16124).
Thanks, @HertzDevil
Temporary variables are now grouped by file name in order to increase the chance of reusing previous macro expansions (#16122).
Thanks, @HertzDevil
Interpreter
Section titled Interpreter- Fully exit the process on
exit!
from REPL (#16171) - Fixed interpreter hanging on the signal pipe (#16167)
Thanks @jneen, @straight-shoota
Infra
Section titled InfraREUSE.toml
describes which licenses apply to which parts of the code base, following the Reuse Software Specification (#15992)- Default to LLVM 16 in
shell.nix
(#16023)
Thanks @straight-shoota, @HertzDevil
Dependencies
Section titled DependenciesThanks @HertzDevil, @ysbaddaden
Deprecations
Section titled DeprecationsSocket#blocking
andIO::FileDescriptor#blocking
properties (#16033, #16129)Atomic::Flag
(#15805)- The
blocking
parameter ofFile.new
,Socket.new
andIO::FileDescriptor.new
constructors (#16034, #16047) Float::Printer::IEEE
(#16050)Process::Status#exit_signal
(#16003)
Thanks @ysbaddaden, @Blacksmoke16, @HertzDevil, @straight-shoota
We have been able to do all of this thanks to the continued support of 84codes 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