Ruby blocks are powerful. You can easily convert an array of numbers to strings:
Of course, you can also do this with this shortcut:
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
method.
This is all very nice but what if you want to pass an argument to the method. For example:
Can we write something like &:modulo(3)
to make it work? It turns out
you can’t, at least
not that easily.
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 has a cache of Procs so they are not created everytime you use the same symbol, but still, it’s slightly slower than a normal block.
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:
For other arguments, we would do something different (for example convert a function type to a block).
Fortunately, waj came with a better proposal: what if we write it like
&.to_s
?
Now, this is a new syntax, different from Ruby. If you do this in Ruby…
irb(main):001:0> [1, 2, 3].map &.to_s SyntaxError: (irb):1: syntax error, unexpected '.' [1, 2, 3].map &.to_s ^
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:
Not only that, but you can also write this:
Or this:
And of course this:
The best thing is that this is just a syntax rewrite without any performance penalty.
comments powered by Disqus