---
title: Crystal 0.17.0 released!
version: 0.17.0
summary: Named tuples
thumbnail: +
author: asterite
---

[Crystal 0.17.0](https://github.com/crystal-lang/crystal/releases/tag/0.17.0) has been released!

This release includes a bunch of nice features: named tuples, double splats, a new
algorithm for method arguments, and `as?`.

Before introducing named tuples, let's remember what tuples are.

## Tuples

You can think of a tuple as an immutable compile-time equivalent of an Array. The compiler
knows its size and the type in each position. A tuple can't be modified.

```ruby
# This is an Array
array = [1, "hi"]
array[0] # => 1       (compile-time type is Int32 | String)
array[1] # => "hi"    (compile-time type is Int32 | String)
array[2] # IndexError (runtime exception)

# We can change the array
array[0] = 2 # OK

# This is a Tuple
tuple = {1, "hi"}
tuple[0] # => 1       (compile-time type is Int32)
tuple[1] # => "hi"    (compile-time type is String)
tuple[2] # compile-time error

# We can't change a tuple
tuple[0] = 2 # undefined method `[]=` for Tuple
```

Note that in the last access we get a compile time error, because the compiler knows
the size of the tuple it knows that that is always going to be an error.

A method can specify a splat argument, and extra call arguments will be places in it,
as a Tuple:

```ruby
def foo(x, y, *other)
  # Return the tuple
  other
end

# Here 3, "foo" and "bar" are captured in the other
# argument, as a Tuple
other = foo 1, 2, 3, "foo", "bar"
other # : Tuple(Int32, String, String)
```

A tuple can also be splatted into method arguments:

```ruby
def foo(x, y)
  x - y
end

tup = {10, 3}

# Here we "unpack" the tuple into arguments
foo(*tup) # => 7
```

A tuple is a struct, and as such it's allocated on the stack and doesn't involves heap
allocations nor puts pressure on the GC.

A Tuple is a generic type, `Tuple(*T)`, with `T` being a tuple of types, but that's the only
special thing about it. It has its own type, documented [here](http://crystal-lang.org/api/Tuple.html)
and you can reopen and add methods to it. For example, if you `require "json"` you can serialize
a tuple to json:

```ruby
require "json"

{1, 2}.to_json # => "[1, 2]"
```

## Named tuples

Now that we know the difference between Array and Tuple we are ready to learn about what
named tuples are.

You can think of a named tuple as an immutable compile-time equivalent of a Hash, with
symbols as its keys. The compiler knows its keys and what type corresponds to each key.
A named tuple can't be modified.

```ruby
# This is a Hash
hash = {:foo => "hello", :bar => 2}
hash[:foo] # => "hello" (compile-time type is String | Int32)
hash[:bar] # => 2       (compile-time type is String | Int32)
hash[:baz] # KeyError   (runtime exception)

# We can change a hash
hash[:foo] = "bye" # OK

# This is a NamedTuple
tuple = {foo: "hello", bar: 2}
tuple[:foo] # => "hello" (compile-time type is String)
tuple[:bar] # => 2       (compile-time type is Int32)
tuple[:baz] # compile-time error

# We can't change a named tuple
tuple[:foo] = "bye" # undefined method `[]=` for NamedTuple
```

Note: if you come from Ruby, you might know that `{foo: "hello"}` denotes a Hash with a symbol
key `:foo` and value `"hello"`. This is different in Crystal, it denotes a NamedTuple.

Also note that, similar to what happens with tuples, when indexing with a key that's not present
in the named tuple the compiler can give a compile-time error. So, in a way, a named tuple is
also a type-safe (or maybe "name-safe") equivalent of an immutable Hash.

At this point you might be thinking why members are accessed like `tuple[:foo]` and not
`tuple.foo`. One reason is that a named tuple has methods, like `size`, which returns
the number of elements in it, and so `{size: 10}.size` would be confusing: is it accessing
the `size` value, or is it asking for the number of elements in it? With the hash-like access
there's no such confusion. The other reason is that in this way a named tuple indeed looks like
an (immutable) Hash, and behaviour is similar to a tuple, where elements are also accessed
in a hash-like (or array-like) way.

The similarities with Tuple continue. We can specify a double splat in a method argument to
capture extra named arguments:

```ruby
def foo(x, y, **other)
  # Return the named tuple
  other
end

# Here 1 matches x, y matches y, and the rest (z and w)
# go to other
other = foo 1, z: 3, y: 4, w: 5
other # => {z: 3, w: 5}
```

A named tuple can also be splatted into method arguments:

```ruby
def foo(x, y)
  x - y
end

tup = {y: 3, x: 10}
foo(**tup) # => 7
```

A named tuple is a struct, and as such it's allocated on the stack and doesn't involves heap
allocations nor puts pressure on the GC.

A NamedTuple is a generic type, `NamedTuple(**T)`, with `T` being a named tuple of types,
but that's the only special thing about it. It has its own type, documented
[here](http://crystal-lang.org/api/NamedTuple.html) and you can reopen and add methods to it.
For example, if you `require "json"` you can serialize a named tuple to json:

```ruby
require "json"

{x: 1, y: 2}.to_json # => %({"x": 1, "y": 2})
```

Here we can generate a JSON with known keys without having to allocate heap
memory for a Hash.

## Tuples and named tuples in action

Tuples and named tuples are very useful data structures. They allow you to group values,
either by position or by name, in an efficient way, and without you having to declare
new types for them, while still preserving type and name safety.

They can also be used to define delegator methods, methods that simply forward all arguments:

```ruby
def foo(x, y, z)
  x*y - z
end

def forwarder(*args, **nargs)
  foo(*args, **nargs)
end

forwarder 10, z: 3, y: 2 # => 10*2 - 3 = 17
```

## The new algorithm of method arguments

We were actually going to adopt a different algorithm for matching call arguments to method
arguments, in a way similar to Ruby, but [BlaXpirit](https://github.com/BlaXpirit) suggested
that we might be interested in
[how Python 3 works in this regard](https://github.com/crystal-lang/crystal/pull/2580#issuecomment-218612032).
And so we decided to implement it in a way very similar to that.

With this we want to say that we always listen and consider all your proposals and suggestions. Then,
of course, we accept those that we think will fit better with the language's philosophy. So keep your
suggestions, critics and comments coming, as they can have a huge impact on the final shape of the language
(like what happened with this particular subject.)

The detailed algorithm is explained [here](http://crystal-lang.org/docs/syntax_and_semantics/default_values_named_arguments_splats_tuples_and_overloading.html),
but a few highlights of it include the ability to force arguments to be passed as named arguments, and to overload
based on required named arguments. For example:

```ruby
# Two positional arguments allowed, z must be passed as a named argument
def foo(x, y, *, z)
end

foo 1, 2       # Error, missing argument: z
foo 1, 2, 3    # Error, wrong number of arguments (given 3, expected 2)
foo 1, 2, z: 3 # OK

# This is another overload: because arguments after * must be
# passed by name, they are part of a method's signature.
def foo(x, y, *, w)
end

foo 1, 2, w: 3 # calls the method above
```

With this, APIs can be crafted with a richer semantic and readablity. For example there's the `spawn` method
in the standard library to spawn a fiber:

```ruby
spawn do
  # work
end
```

There's also the `spawn` macro that receives a call as an argument and spawn a fiber that invokes that method:

```ruby
spawn work(1)
```

We always wanted to have a way to spawn a fiber with a name associated to it. The problem is that `spawn(name) {}`
would conflict with the macro call above (macros don't overload based on whether a block was given to them).
We could define `spawn_named(name) { }`, but that doesn't look very nice.
With this new feature we can define it with a required named argument:

```ruby
def spawn(*, name : String)
  # ...
end

spawn(name: "worker") do
  # ...
end
```

There are many other situations where required named arguments allow overloading where just the number of
arguments and their type is not enough.

## External names

Arguments can now also have an external name associated with them, making it possible to use keywords
as named arguments, and to have a small readablity boost:

```ruby
# OK, but reads odd
def increment(value, by)
  value + by
end

# Better: you can use `by` when invoking the method,
# and `amount` inside the method body
def increment(value, by amount)
  value + amount
end

increment 1, 2     # => 3
increment 1, by: 2 # => 3
```

## The as? pseudo-method

Similar to [as](http://crystal-lang.org/docs/syntax_and_semantics/as.html), `as?` casts an expression
to a given type, if it's of that type, and otherwise returns `nil`. In a way, it's a safe cast (`as`
raises a runtime exception instead of returning `nil`):

```ruby
value = rand < 0.5 ? -3 : nil
result = value.as?(Int32) || 10

value.as?(Int32).try &.abs
```

## Final words

Like in previous releases, this release focuses on better readablity and better expressive power
while retaining type and name safety, and performance. The next releases will probably focus more
on the language's stability (there are a couple of bugs related to generic types, nothing that
can't be fixed), and improving the concurrency model.

> **THANKS:**
> We'd like to thank everyone for their continued support, be it in the form of pull requests,
> bug reports, bug fixes, comments, suggestions or ~~[donations](https://salt.bountysource.com/teams/crystal-lang)~~.
> There's no way we could have made it so far without you. Happy Crystalling! <3
