lihaoyi / macropy

Macros in Python: quasiquotes, case classes, LINQ and more!
3.28k stars 176 forks source link

Macro Expansion Script #62

Closed reem closed 6 years ago

reem commented 10 years ago

Some form of preprocessor script that could be run on files with macros to make them directly runnable by replacing all macro code with the actual generated code.

This would allow:

  1. Packaging of scripts as directly runnable
  2. Removal of macropy as a dependency for distributed code
  3. Allows for macros to be used in projects that don't want another dependency as code-generation tools for individual developers

To take an example from the README:

point.py:

from macropy.case_classes import macros, case

@case
class Point(x, y): pass

CLI:

$ python expand_macros.py point.py

exp_point.py:

class Point(object):
    __slots__ = ['x', 'y']
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return "Point(" + self.x + ", " + self.y + ")"

    def __repr__(self):
        return self.__str__()

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __ne__(self, other):
        return not self.__eq__(other)

    def __iter__(self, other):
        yield self.x
        yield self.y

I for one would love to be able to use macros like this to generate boilerplate code but still maintain my scripts as directly runnable and easily distributable.

reem commented 10 years ago

Ah geez, I'm blind. Found the SaveExporter....

lihaoyi commented 10 years ago

Yeah, the docs could be better.

Unfortunately, due to how Python's import process works (e.g. everything is 100% dynamic and at run time) It's basically impossible to go through a python source tree and expand all the macros with the same semantics as a "natural" just-in-time expansion, short of actually executing everything and waiting for the macros to be expanded as they are found. That's why I don't have a "point at file and expand macros" button, and that's why you have to do the weird "activate SaveExporter and run your test suite/code to save all expanded code" thing in order to save the macro-expanded files.

I'm also not using SaveExporter at all for my own work, and it's only there because I thought it was a neat/cool idea. Hence it's probably much less polished than macropy/core or the macros themselves. Let me know if you have any problems =)

On Mon, Nov 25, 2013 at 2:03 PM, reem notifications@github.com wrote:

Closed #62 https://github.com/lihaoyi/macropy/issues/62.

— Reply to this email directly or view it on GitHubhttps://github.com/lihaoyi/macropy/issues/62 .

reem commented 10 years ago

What if macropy were to integrate with doctest?

Since you have to run the functions/code to be able to expand macros, why not just use doctest-style comments to provide valid arguments for each function/piece of code that you want to expand and then have some additional overhead that just saves the expanded code? To tell which macros the snippet relies on you could just mandate that the first line of the doctest has to be from ... import macros, ....

This gets around the "Python is too dynamic" issue because it lets you actually run code while still allowing for expansion to be fast because the user can intentionally provide all the arguments necessary to run the code quickly.

lihaoyi commented 10 years ago

It's not so much that you need to run the functions/code to expand the macros in file.py, it's that you need to run macro.py files to expand the macros, and the behavior of macro.py can be arbitrarily dependent on the state of the world when file.py was imported. So you won't need to stub out args to functions in file.py, you'll rather need to stub out the rest of the world in macro.py's invocation, a somewhat more difficult task.

Furthermore, macro.py could itself depend on other macros. In macropy's own macros, these macro dependency chains go something like 3-4 deep e.g. quasiquotea -> quick_lambda -> case_classes -> macropeg. They're statically analyzable (since macro imports can only be top-level) which is nice, but it's still a pretty messy business.

We could always decide arbitrarily that pre-compiled code will have such-and-such a bootstrap sequence to get the macros ready, but then the semantics of the pre-expanded programs would diverge from the semantics of the JIT-expanded programs in arbitrary ways, which is non ideal.

Perhaps you could come up with something and see if it works for you? I'm not using MacroPy at the moment, so developing new features based on my imagination of other people's real-world needs is pretty tough. If it works well for you and looks good, I'd be happy to include it into MacroPy itself

azazel75 commented 6 years ago

Nothing had come up since 2013, so I'm closing this