Quick background: I am designing a Pythonic language that I want to be as powerful as Lisp while remaining easy to use. And by "powerful", I mean "flexible and expressive".

I've just been introduced to Python function decorators, and I like them. I found this question that suggests decorators are not as powerful as Lisp macros. The third answer to this question says that the reason they're not is because they can only operate on functions.

But what if you could use decorators on arbitrary expressions? (In my language, expressions are first-class, just like functions and everything else, so doing that would be fairly natural.) Would that make decorators as powerful as Lisp macros? Or would I have to use syntax rules, like Racket and Honu, or some other technique?


I was asked to provide an example of the syntactic construct. For an "expression decorator", it would simply be a decorator on an expression (where "$(...)" creates an expression object):

$(if (true):
    printf("Was true")
    printf("Wasn't true"))

Also, if I do need to use syntax rules, as mentioned above, it would use a pattern matching syntax:

# Match a bool expression, a comma, and a string.
macro assert(expr:bool lit string) { e , s }:
    if (!e):
        error s
        printf("All good")

The above would match this:

assert i == 0, "i should have been 0"

That's a rough sketch, but it should give you an idea.

  • 1
    Perhaps you can provide an example in your question of the syntactic construct you are describing, and explain how it works. Part of what makes Lisp so powerful is its homoiconicity so, if you'd like us to evaluate your language feature on it's own terms, I think we're going to need to see it. Aug 8, 2014 at 4:01
  • 1
    @RobertHarvey Homoiconicity is nice (and as a Schemer, I love it and all), but Scheme macros have shown that with a good pattern-matching system, homoiconicity is not really required for real macros. By way of example, sweet.js provides a pattern-matching macro system that works on similar principles to Scheme macros, and JavaScript is obviously not homoiconic. (Of course, the downside is that sweet.js works on a preprocessing basis, and is not an embedded DSL like Scheme macros are.) Aug 8, 2014 at 4:09
  • Your examples just looks like an idiosyncratic way to spell macro invocation (that doesn't mesh well with the offside rule by the way). It certainly has nothing to do with decorators, so there's no need to lean on the syntax.
    – user7043
    Aug 8, 2014 at 5:06
  • @delnan Offside rule? I have no idea what that is. Aug 8, 2014 at 6:06
  • @gdhoward Indentation used to delimit blocks. "Significant whitespace.
    – user7043
    Aug 8, 2014 at 10:05

1 Answer 1


Short answer: You can't.

Not in Python or anything close to what it is today, at any rate. The reason is straightforward: Functions are objects. Expressions are not. Expressions describe activities--the activity of operating on objects and producing a result. While the input and output of expressions are objects, the expressions themselves are not. Because they are not objects, they cannot be decorated.

This limitation on Python metaprogramming is pretty baked into the language. It's not impossible to change Python semantics. Modules like forbiddenfruit let you monkeypatch built in objects in ways that are officially "impossible." Modules like MacroPy, karnickel, and the now apparently defunct MetaPython do provide some macro facilities through AST rewriting and related techniques. None of these is certified for production use, however, and most of them pop up, support one or two language iterations, and then evaporate. The double-back-flip-with-twist moves necessary to make them ever work at all just are not sustainable over any period of time, in any generality, or in the context of "code you can depend on."

There are modules that successfully use introspection and reflection to inspect what is currently happening in a program. My say module, for example, works well across many Python 2 and Python 3 versions, and even alternate Python implementations like PyPy. But few Python meta-modules can actually change program semantics. GvR has been pretty resistant to metaprogramming facilities. The with statement semantics, for example, were constrained for the particular purpose of limiting how much metaprogramming it can support.

But, that's Python as it exists today. You might imagine your Python++ language having arbitrary code blocks (the mythical multi-line lambda, often mentioned but never actually seen in Python) that can be decorated. Lambdas are objects, and code blocks are very object-ify-able. You probably would not want the overhead of making every expression into an object, but it's not hard to imagine an environment in which any expression that was interesting to encode in a block could be done so, then decorated.

Two standard (i.e. non-Lispy) and quite Pythonic languages that have worked to integrate metaprogramming and advanced macro facilities are Julia and Nim (formerly known as Nimrod). The way they address program representation and transformation are very powerful, and would be good study points for your design.

  • You're right; expressions aren't objects. I think I meant "continuations". At least, that's what I think they are. Regardless, good answer. +1 and accepted. Aug 16, 2014 at 2:54
  • The fact that the AST is exposed has been a boon to developers. If you want to commit yourself to a Lispy solution on the Python platform, there's always Hy (Hylang.org), which basically is Lisp with a Python AST backend. Dec 11, 2014 at 23:47
  • 1
    Hy is a cool trick, but working in it is "working in Lisp" not "working in Python." It may piggyback the Python infrastructure and ecosystem just as Jython and JRuby piggyback the Java infrastructure and ecosystem, but it doesn't seem to let you do anything more in Python that you couldn't do before Hy. Dec 12, 2014 at 0:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.