---
title: to_proc
thumbnail: "&."
summary: Shortcut syntax for writing one-argument blocks
author: asterite
categories: technical
tags: language
---

## Ruby to_proc

Ruby blocks are powerful. You can easily convert an array of numbers to strings:

```ruby
[1, 2, 3].map { |n| n.to_s } #=> ["1", "2", "3"]
```

Of course, you can also do this with this shortcut:

```ruby
[1, 2, 3].map &:to_s #=> ["1", "2", "3"]
```

The `&` operator converts an object to a Proc suitable to be
passed as a block. You can make any class respond to this operator by implementing
a `to_proc` method. Symbol has a [to_proc](http://ruby-doc.org/core-2.0.0/Symbol.html#method-i-to_proc)
method.

This is all very nice but what if you want to pass an argument to the method. For example:

```ruby
[10, 20, 30].map { |n| n.modulo(3) } #=> [1, 2, 0]
```

Can we write something like `&:modulo(3)` to make it work? It turns out
[you can't](http://stackoverflow.com/questions/9932678/how-do-you-pass-an-argument-to-ruby-array-map-short-cut), at least
[not that easily](http://iain.nl/going-crazy-with-to_proc).

Not only that, but since Ruby has to convert the Symbol to a Proc there's a slight performance
penalty over doing it with a normal block.

Finally, Ruby's implementation of [Symbol#to_proc](http://ruby-doc.org/core-2.0.0/Symbol.html#method-i-to_proc)
has a cache of Procs so they are not created every time you use the same symbol, but still, it's slightly
slower than a normal block.

## Crystal to_proc?

At first we thought about making Crystal have the same syntax for this, but a bit hacky: if you
do `&:to_s`, because the argument to `&` is a Symbol we can rewrite the source code to receive a block:

```ruby
# This:
[1, 2, 3].map &:to_s

# is rewritten to this:
[1, 2, 3].map { |x| x.to_s }
```

For other arguments, we would do something different (for example convert a function type to a block).

Fortunately, [waj](https://github.com/waj) came with a better proposal: what if we write it like
`&.to_s`?

```ruby
[1, 2, 3].map &.to_s
```

Now, this is a new syntax, different from Ruby. If you do this in Ruby...

<pre class="code">
irb(main):001:0> [1, 2, 3].map &.to_s
SyntaxError: (irb):1: syntax error, unexpected '.'
[1, 2, 3].map &.to_s
               ^
</pre>

This means that placing a dot after the `&` makes no sense in Ruby, which also means that this syntax
is available for giving it a new meaning. So in Crystal we chose to use this syntax instead.

With this little change, we can pass arguments to the method very easily:

```ruby
[10, 20, 30].map &.modulo(3) #=> [1, 2, 0] ... but only in Crystal ;-)
```

Not only that, but you can also write this:

```ruby
[1, 20, 300].map &.to_s.size #=> 1, 2, 3
```

Or this:

```ruby
[[1, -2], [-3, -4]].map(&.map(&.abs)) #=> [[1, 2], [3, 4]]
```

And of course this:

```ruby
[1, 2, 3, 4].map &.**(2) #=> [1, 4, 9, 16]
```

The best thing is that this is just a syntax rewrite without any performance penalty.
