Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macro shared state #3604

Closed
kostya opened this issue Nov 29, 2016 · 4 comments · Fixed by #3612
Closed

macro shared state #3604

kostya opened this issue Nov 29, 2016 · 4 comments · Fixed by #3612

Comments

@kostya
Copy link
Contributor

kostya commented Nov 29, 2016

would be nice if macro can have shared variables, and callback after program readed and macro expanded (macro class_finished or macro program_finished).
this allow things like #2987 or nice dsl, api generators, based on user usage:

shard

class Api
  macro mapping(tuple)
    {% $data ||= [] of NamedTupleLiteral %}
    {% $data << data %}
  end

  mapping({a : Int32, b : String})
end

macro program_finished
  class Api::Data
    JSON.mapping({
    {% for data in $data %}
       {{data}}
    {% end %}
    )}
  end
end

user program

class UserClass < Api
  mapping({c : Float64})
end
@asterite
Copy link
Member

A long time ago I wasn't sure about this, having stuff change at compile time. However, Elixir does this exact same thing. Well, it's a bit different, they use attributes which are available at compile time, but one can override them and use their previous values to create new ones (I guess mostly because everything's immutable). And then they have _before_compile.

In fact, in Crystal you can already have shared state. We are just missing the macro at_exit or a better name. For example, this works:

require "json"

class Foo
  MAPPING = {} of Nil => Nil

  macro mapping(**values)
    {% for key, value in values %}
      {% MAPPING[key] = value %}
    {% end %}
  end

  macro close_mapping
    JSON.mapping({{MAPPING}})
  end

  mapping x: Int32, y: Int32
end

class Foo
  mapping z: Int32?
end

class Foo
  close_mapping
end

p Foo.from_json(%<{"x": 1, "y": 2}>)
p Foo.from_json(%<{"x": 1, "y": 2, "z": 3}>)

play

We just need to define the name for this macro. It's a new macro hook, that will be executed, I think, right after all the top level declarations and methods are processed, right before instance vars types are checked, etc.

@bcardiff
Copy link
Member

I think before_compile is a good name to go.

There was an issue that since the constant can't change the value it reference to, then for value types we need to boxed them. But even with that limitation I think it's good to add this.

Also someone can hack around with run command and save state in temp files and read them before compiling 😨 ... someone will eventually do it ... :-P

@asterite
Copy link
Member

Actually, a name in the past tense would be optimal, so it's on par with included, inherited, extended. Ideas?

@jessedoyle
Copy link
Contributor

jessedoyle commented Dec 1, 2016

Maybe compiled ?

Edit: actually finished works well too!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants