gilch / hissp

It's Python with a Lissp.
https://gitter.im/hissp-lang/community
Apache License 2.0
369 stars 9 forks source link

macroexpansion could be easier #173

Open gilch opened 1 year ago

gilch commented 1 year ago

The current recommendation to invoke the macro using method syntax works, and is maybe adequate for debugging purposes, when you already know the thing you have is a macro (or can easily check). It's also nice that this doesn't require any additional helper functions, like macroexpand, macroexpand-1 or macroexpand-all.

However, that's not good enough for metaprogramming purposes. Advanced macros may need to (perhaps conditionally) pre-expand forms in their body, and there's no easy way to tell if any given form is a macro. You'd have to repeat the same checks the compiler is using:

Then get the macro function, invoke it, and return the result, or don't and return the original form, depending on the answer. Also this should be done in the compiler's macroexpansion context so NS is set properly, just in case the macro function is using that. That last one is already in the right context if you're expanding a macro inside another macro, but maybe not if you're debugging with method syntax.

As one of the design goals of Hissp, compiled Hissp/Lissp code is not supposed to have any run-time dependency on the hissp package, which means the package can't provide any bundled functions for use with Lissp code, only macros. However, helper functions that are only meant to be used at compile time in a macro technically don't break this rule, because they don't appear in the compiled expansion, and as long as the body is never used at run time, Python doesn't care that lookups wouldn't resolve. The bundled macros already do this with helpers in the reader module, like is_hissp_string().

The macroexpand family could be done the same way in the compiler module. There's little run-time use for these, except, perhaps, when debugging at the REPL (if you have a REPL, you have the hissp package too), so it wouldn't violate the design rule. The checks would have to be factored out of the Compiler class to avoid duplicating logic, but I think that's doable.

I'm not seeing very good alternatives. Maybe a helper macro could expand to an inline function definition, or it could be added to the prelude. Maybe macro recognition could be simplified. Other Lisp-1s would put the macros in the same global namespace as the functions, and just mark them somehow. Python function objects can take attrs and annotations, so this would be easy. Having a separate _macro_ namespace for them has certain tradeoffs that I didn't fully understand at the time I designed it that way. I've added workarounds, but it's certainly not the only way it could be done. Changing that now would deeply alter the character of Hissp. It's not something I'd take lightly, but perhaps there is a middle ground? This will take some careful thought.

gilch commented 1 month ago

I think I've got this implemented. I pushed up the branch so far, but it's not tested yet. Writing at least one of #179 or #259 should be sufficient to test and document it. This is the last major feature currently holding up the 0.5 release. I'd still like to read over the documentation and try to improve test coverage before releasing though.