Hacker News new | past | comments | ask | show | jobs | submit login
Macros in Python: quasiquotes, case classes, LINQ and more (github.com/lihaoyi)
93 points by mzl on May 26, 2015 | hide | past | favorite | 18 comments



Looks like a fun project on its own, with a lot of syntax tree hackery going on, but I see no real (pragmatic) use since many of the AST hacks and use cases presented are in my opinion completely unwarranted and can already be solved by the existing machinery (perhaps with quite heavy use of the metaclasses).

As an example, instead of hacking the AST via macros:

    @case
    Point(x, y): pass
one could just do a (this example implies, of course, that there is a CaseClassMeta somewhere attached to CaseClass):

    class Point(CaseClass):
        fields = 'x', 'y'
Not only this is nicer, you retain full control of the MRO on the metaclass level and can adjust its behaviour as needed and when needed.

Enums, complex enums and class body nesting examples -- can also be solved via metaclasses quite easily.

Quick lambdas example -- is somewhat close to "whatever" Python library, this is probably the one case which cannot be reproduced as is without AST hacks.


The "whatever" library looks really neat -- thanks for the reference:

https://pypi.python.org/pypi/whatever


Yes, the use of macros is not always justified in the examples, because Python is quite expressive already.

However, the ability to have macros is orthogonal with the feature set of the language. What is interesting is the ability to inspect and modify a Python AST.

Of course, meta-programming is a way to integrate otherwise absent features of a language as-if they were built-in. Python is sufficiently dynamic that most things can be done using existing facilities, like reflection and metaobjects (the @case example, as you mention).

But there is a difference between using those facilites at runtime and producing some code at macro-expansion time (this might be relevant for Cython, for example).

You can, for example, define a domain-specific language that is directly translated as Python, and not interpreted like an API-based representation would. You can even apply custom type checking for that DSL.

Or, you could analyze an existing python source code and produce a translation in another language without having to reimplement a parser (see the Python-to-Javascript example, or Common-Lisp's Parenscript).


How would you resolve the tail call optimization he pointed out, without writing your own loops?


And one could use complex, or quaternions to represent points in order to not have to reinvent the wheels of rotation/translation/computing area/projections/polar to cartesian ...

Here is my sokal version of class point; just repr overload for complex: https://gist.github.com/jul/9286835

I think the class point is the proof OOP is not able to give a real use case for itself other than reimplementing the wheel poorly.

OOP has proven to be confusing the youngs and lazy. It makes people confused about abstraction and the concrete world (map and territory).

It is pedantic, and inefficient.

EDIT: But the code of this guy is still amazing, even if totally pushing in wrong directions sometimes: memoization is the worst caching strategy possible. On the other hand the PEG parser is brilliant, like most of the code.


Some of these things look really nice. I'm particularly impressed by the tracing macro:

    with trace:
        sum = 0
        for i in range(0, 5):
            sum = sum + 5
prints

    sum = 0
    for i in range(0, 5):
        sum = sum + 5
    range(0, 5) -> [0, 1, 2, 3, 4]
    sum = sum + 5
    sum + 5 -> 5
    sum = sum + 5
    sum + 5 -> 10
    sum = sum + 5
    sum + 5 -> 15
    sum = sum + 5
    sum + 5 -> 20
    sum = sum + 5
    sum + 5 -> 25
This would save a huge amount of boring typing `print(...)` when debugging, and since it's only for debugging you don't even need to have MacroPy as a dependency!

Unfortunately python 3.4 support is not quite ready [0], and attempting to install it with `pip3` just fails.

[0]: https://github.com/lihaoyi/macropy/issues/32


Can I recommend the wonderful `q`?

https://pypi.python.org/pypi/q

"import q; q(var1, var2)" forces printing to $TMPDIR/q (usually /tmp/q) very detailed output of what you are tracing and what it is.

The @q decorator does somewhat the same as the "with trace" context manager.


It seems nice, but not quite as powerful. I like the way the tracing macro automatically prints every intermediate value, but q doesn't seem to do that.


The real question is, why are you using print statements to debug?


AST transformation is nice but you can also have magic macros using `coding` hack. It could add arbitrary syntax to python

https://github.com/syrusakbary/interpy

previously https://news.ycombinator.com/item?id=9340784


I'm a little confused where you are going with this. Could you elaborate a little? Its one thing for this to be possible using `coding`, but this is a macro builder available right now. Interpy just highlights the possibility of this, right? If there were a macro framework that used `coding` it would seem more relevant. Otherwise it looks like you're comparing apples and a knife. Am I missing something?


> If there were a macro framework that used `coding`

Does JSX-like template for Python count?

https://github.com/dropbox/pyxl

    # coding: pyxl
    print <html><body>Hello {my_var}!</body></html>
Combined with this, we can make Meteorjs-like applications in Python directly. Even AST transformation of backend DOM operations in Python directly rendered to Javascript to browser.


Ah! Sorry about that. As I said, I was confused. That is very interesting.


A few years ago I wrote a similar thing: https://github.com/mmikulicic/metacontext (now unmaintained)


It's a cool project, but I think it needs more examples on the front page. The class example might as well have been a namedtuple.


Their argument [0] against namedtuple is that the namedtuple implementation is basically a hackier macro anyway (format a string then eval it). But I agree that it's not the most impressive example of what you can do with macros (see my other post).

[0]: https://github.com/lihaoyi/macropy#skeletons-in-the-closet


Forgive my ignorant question but why do their examples only work in REPL?


It works in or out of the REPL (in fact, they document "MacroPy also works in the REPL"). It's just standard-ish in Python documentation to give examples formatted the way the REPL would display them.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: