In the macro definition (macro for(expr, in _in, &block)
), the only special tokens/keywords are macro
and &
. I’ll set those aside because they’re not what I’m trying to explain, but my point is that neither in
nor _in
is treated as a keyword in that definition.
I’ll see if I can explain using a more general example. Say I want to take an array and output all of the elements starting at a lower index and ending at a higher index. I might implement it like this[1]:
def output_range(array, start_index, end_index)
start_index.upto(end_index) do |index|
puts array[index]
end
end
and then I (or someone else) could use it like
arr = [1, 2, 3, 4, 5] of Int32
start_i = 1
end_i = 3
output_range arr, start_i, end_i
getting the output
2
3
4
Seems like I succeeded in my goal. However, I don’t like how I’m just passing in unlabeled values. If I were to call the method like
output_range arr, 1, 3
then maybe someone (including me) reading my code could puzzle out that the 1
and 3
are the start and end indices, respectively, but I’d rather they just knew. So I’ll label them, using the existing parameter names[2]:
output_range arr, start_index: 1, end_index: 3
Much better. I think it’s pretty wordy, though, so I’d rather change the parameter names so that the method call labels are shorter and more natural. So I’ll just change the method:
def output_range(array, from, to)
from.upto(to) do |index|
puts array[index]
end
end
arr = [1, 2, 3, 4, 5] of Int32
output_range arr, from: 1, to: 3
But now I don’t like how my method looks. from.upto(to)
doesn’t read very naturally. I’d rather have the old name in the method and the new name when I’m calling the method. And that’s what I can do, with the same syntax that in _in
uses:
def output_range(array, from start_index, to end_index)
start_index.upto(end_index) do |index|
puts array[index]
end
end
arr = [1, 2, 3, 4, 5] of Int32
output_range arr, from: 1, to: 3
So, at the end of the day, this syntax exists primarily to let you separate the naming inside and outside of your method to allow for code that reads more naturally as English (or presumably whatever language you’re coding in).
The in _in
case is a little special, because we can’t actually have a variable called in
because that’s a keyword. However, the label isn’t a real variable, so we can use it to hide the ugly underscore we have to use internally.
I know that was a long explanation. Does it make any more sense now?
[1] There are, of course, easier ways of doing this in the standard library.
[2] You can do this with any method parameter in Crystal. You can even change the order of labelled arguments, if you want.