For macro

Hi. I have been a ruby user (since version 1.8) and just started using crystal. I appreciate developer’s effort.

I found “for” macro from https://gist.github.com/bcardiff/05482bb9dddeacf41856 but it does not work because of the following error (using crystal 1.1.1):

In for.cr:8:7

 8 | for e in 1..5 do
           ^
Error: expecting token 'EOF', not 'in'

I would like to know how to fix it.

Thank you, in advance.

Possibly stupid comments:
Actually, I found that it works fine in the following unshapely code:

macro for(expr)
  {{expr.args.first.args.first}}.each do |{{expr.name.id}}|
    {{expr.args.first.block.body}}
  end
end

for e _in (1..5) do
  puts e
end

for e _in [1,2,3,4] do
  puts e
end

I also found “for” macro somewhere else but I could not find again…
I know that “for” is not a standard syntax in crystal/ruby but I am glad of keeping “for” syntax because

  • it is easy to understand loop structure visually beginning with “for”,
  • it is easy for programmers who are unfamiliar with crystal (specifically C programmers) and understanding a code as a pseudo-language.

in became a keyword recently. I think it should be a valid identifier in this context, though. The compiler is maybe too restrictive.

This would be a better readable workaround btw.:

macro for(expr)
  {{expr.named_args.find{|a| a.name == "in" }.value }}.each do |{{expr.name.id}}|
    {{expr.block.body}}
  end
end

for e in: (1..5) do
  puts e
end

for e in: [1,2,3,4] do
  puts e
end

Thank you for your reply. I feel “in:” is better than “_in” :slightly_smiling_face: .
Actually, I have tried using “\in” that comes from TeX notation, but it did not work fine :cry: .

I apologize for multiple reply. Maybe we want something escaping keywords. How about introducing a syntax like this?

macro for(v,"in",e,block)
  {{e}}.each do |{{v}}|
    {{block}}
  end
end

The constant string “in” represents a redundant word escaping keywords. I feel that it makes macros more expressive. Of course, we should prepare something for the case where crystal cannot find the redundant word. I apologize if it is impossible. Actually, I don’t know how macro syntax is implemented in crystal…

I would vote this isn’t worth it. There aren’t too many keywords and you’re just asking for trouble trying to get them to work. Needing to remember to “escape” it everytime you want to use it is arguably worse than just using a diff name.

I’d also argue that just using a normal .each is much more readable, esp for those not familiar with your code.

2 Likes

Thank you for your reply. My last post was just a mumble proposing a general solution introducing redundant words. Now, I am using .each and I agree that we should use .each especially when the result is substituted or used as a return value. However, it is hard to throw away beginning with “for” similarly to C :slightly_smiling_face: . Finally, I will follow developer’s decision.

PS: After my first post, I found a similar topic in the forum. I apologize for repetition.

1 Like

You can use an external parameter name:

macro for(expr, in _in, &block)
  {{ _in }}.each do |{{ expr.name.id }}|
    {{ block.body }}
  end
end

for e, in: (1..5) do
  puts e
end

for e, in: [1, 2, 3, 4] do
  puts e
end

Also note that for loops are actually available in the macro language (due to other syntax limitations).

Thank you for providing an alternative solution. My ideal is using “in” similarly to ruby but it might help someone who want to use for loops. Actually, I have just started to understand macro language and I could not understand what “in _in” means. I will read documents carefully.

There’s no need to use for, and in Ruby for is discouraged.

3 Likes

A few notes about the following code:

macro for(expr, in _in, &block)

or

def for(expr, in _in, &block)

The in _in is two different identifiers. The first one (in) is the external identifier. As you can see above, you can use in: when calling the method to specify that the argument following corresponds to that method parameter. The second identifier in the definition (_in) is the internal identifier, which refers to that parameter within the method/macro definition. You can take another look at @HertzDevil’s code and see how that works in that particular case.

The basic summary: if you have def fun(a b), b is used within the function (or macro) definition, and a: is used outside. You can read more in the documentation.

Also, &block is a captured block/proc.

Thank you for your reply.

(First of all, I would like to mention that, as written in my first post, I know that “for” syntax is not standard and I don’t stick at using “for” (now I am using “.each”). I would like to write my opinion in the next post.)

Actually, I could not understand the meaning of “in _in” in the document (maybe “block” is OK). I have two questions that I could not find in the document. I hope that it is informative for other readers.

  1. The first one (in) is a keyword? If it is, please let me know how to use it (in) (maybe I understand the role of the second one (_in).
  2. Please let me know the reason why two words should be separated by the white space " ".

Thank you, in advance.

The following is just my personal (non-standard) opinion. Please, developers, ignore this.

Just in my personal ruby code (not intended to be disclosed), I had used the both “for” and “.each” as follows:

  • I used “for” for the future when my code will be converted to C’s “for” syntax.
  • I used “.each” when it should be converted to a function in C code.

In other words, I had used ruby as

  • a “better perl” for text processing (actually I had used perl before ruby), and
  • a “much better C (or better C++ :slightly_smiling_face:)” for the development of prototype.

Actually, I have tried to use nim, but it seems not be satisfying the first one (actually, I don’t like the off-side rule :slightly_smiling_face:). Although the second one could be unnecessary by using crystal :slightly_smiling_face:, I am glad of keeping C-like syntax to describe a code in procedural language (pseudo language).

I am sometimes unsatisfied with ruby’s “for” because of the scope of a loop variable (now we can use local loop variables in C11). I found in somewhere that it is solved by using crystal’s macro. Actually, it is the reason why I am interested in crystal. I found that there was an error in for.cr and I just wanted to know whether it comes from developer’s decision or a bug in the implementation of macro syntax.

I hope that crystal keeps its flexible syntax using macros. I believe that non-standard macros and open classes are allowed in a private code (application) although it should not be in a standard library. I think they are advantages of crystal/ruby against other languages (e.g. C++ and python).

I apologize for a long post. I would like to close this topic.

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.

1 Like

Thank you for your specific explanation. I could understand what you explain after reading External names (I think it could be informative for other readers). Actually, I could not find this document before because I had read “external name” are general words, but it is my fault. I will try to use labeled arguments and external names when they are necessary. Than you so much again.

I apologize for many posts.

I have considered what is the best alternative of “for” syntax in crystal (version 1.1.1). Of course, crystal masters’ suggestions are sufficient but I would like to summarize alternative (non-standard) solutions.

## non-standard style in crystal

macro for(expr)
  {{expr.args.first.args.first}}.each do |{{expr.name.id}}|
    {{expr.args.first.block.body}}
  end
end

# breaking a standard by using a unicode (non-ascii) character (maybe crystal never introduces non-ascii keywords)
for e ∈ (0...10) do
  puts e
end

# introducing an idiom that underscore prefix indicates a mathematical symbol (we can also follow crystal masters' suggestions)
for e _in (0...10) do
  puts e
end

# borrowing "within" introduced in the previous discussion "Macro not working anymore"
for e within (0...10) do
  puts e
end

# someone may prefer the following (we can use "mof" in short)
for e member_of (0...10) do
  puts e
end

## standard style in crystal/ruby
10.times do |e|
  puts e
end

0.upto(9) do |e|
  puts e
end

(0...10).each do |e|
  puts e
end

Of course, the above codes are equivalent in crystal (version 1.1.1). I hope that the above suggestions encourage using crystal who want to use “for” syntax. I would like to thank for developers implementing powerful macro.

PS: Maybe I will use “.each” syntax in practice :slightly_smiling_face: . When I want to describe a pseudo code, I will use the first one.