The Crystal Programming Language Forum

Strange phenomenon of calling a macro with named parameters (its parameter value is constant)

Example code:

macro key_gen(name)
  "{{name.id}}_id"
end

CHAT = "chat"

k1 = key_gen(name: {{ CHAT }})
k2 = key_gen({{ CHAT }})

puts k1 # => {{ CHAT }}_id
puts k2 # => chat_id

How do I pass a constant when I call it in the first way?

Play: https://play.crystal-lang.org/#/r/7981

I think I added that special rule to the compiler at some point, before we had named arguments… or maybe I didn’t take named arguments into account. I don’t know whether it was a good idea. But please open an issue in our main GitHub repository. Thank you!

1 Like

I remember there are a LOT of unimplemeted macro expansion like that, @asterite you mentioned it while fixing one in a literal a (long?) time ago… Would be nice to implement more cases!

I think we should revert the current behavior. If you really want the value behind the constant you can call resolve on the macro side.

How would that fix anything? I think you misunderstand the issue here, the problem is not the constant, it’s the MacroExpression that is not expanded (https://play.crystal-lang.org/#/r/7a4g)

An alternative is to put that entire call inside a begin macro section. What I mean is that resolving macro expressions at that point is not intuitive or uniform.

What I mean is, we introduced this feature so we can do:

require "json"
require "yaml"

class Foo
  MAPPING = {x: Int32, y: Int32}

  JSON.mapping({{MAPPING}})
  YAML.mapping({{MAPPING}})
end

That is, being able to reuse a mapping. However, this can also be done like this:

require "json"
require "yaml"

class Foo
  MAPPING = {x: Int32, y: Int32}

  {% begin %}
    JSON.mapping({{MAPPING}})
    YAML.mapping({{MAPPING}})
  {% end %}
end

which doesn’t need this extra feature.

Additionally, we now have JSON::Serializable and YAML::Serializable which means the above can be written like this:

require "json"
require "yaml"

class Foo
  include JSON::Serializable
  include YAML::Serializable

  property x : Int32
  property y : Int32
end

so there’s even less need for this kind of macro magic.

That’s why I’m proposing reverting this “feature” when moving forward with the language. It simplifies the language (removes an extra strange rule).

4 Likes