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

Rethink reader macros #174

Open
gilch opened this issue Sep 25, 2022 · 2 comments
Open

Rethink reader macros #174

gilch opened this issue Sep 25, 2022 · 2 comments
Labels
design Notes and ruminations that might lead somewhere

Comments

@gilch
Copy link
Owner

gilch commented Sep 25, 2022

Reader macros originally required fully-qualified names. I've since added unqualified names ending in a hash in the _macro_, and seem to be using those a lot more.

It's a design goal of Hissp that everything be available in-line without advance imports. This is important for lissp -c usage, and any other embedding that just does short snippets. Maybe I should add that to the README. Unfortunately, this isn't possible for the prelude definitions, but lissp -c does imply the prelude. Fully-qualified names work for all importable runtime objects, and for compiler macros. The original fully-qualified reader macros also fit this requirement.

But, it's pretty awkward that using a reader macro that already ends in a hash requires an escape of that hash. E.g.

hissp.._macro_.b\##"Fully qualified b# macro at read time."

It would be nicer if

hissp.._macro_.b#"Fully qualified b# macro at read time."

worked, perhaps by assuming the hash is part of the name. Maybe we don't have to require it to live in a _macro_ namespace anymore, just end in #. But consider

builtins..ord#Q

Now this won't work, because it's ord, not ord# in builtins. But,

.#(ord 'Q)

still would. Maybe we can stop here.


This is both better and worse. It's nice that we don't have to fully qualify it (although we can), but it's too bad we have to wrap it in (), which could force a line break in standard style. ord was never meant to be a reader macro, we're just invoking it that way, so maybe an inject makes more sense.

But consider the alias macro

(hissp.._macro_.alias M: hissp.._macro_)
M:#!b"Read-time b# via alias."

(alias B builtins.)
B#!ord Q

also from the quick start. As a proof of concept, a custom reader macro could be made to work the same way, on any symbol resolvable at read time:

as-reader-macro#!builtins..ord Q
as-reader-macro#!ord Q

But, we'd have to "import" as-reader-macro somehow. The whole point of this form was to make everything available inline without advance imports.

One solution might be to extend the built-in inject macro .# to accept optional extra arguments, as aliases do.

.#!builtins..ord Q
.#!ord Q

This would allow any read-time resolvable callable to be used as a reader macro, but wouldn't necessarily require a fully-qualified name. This frees up the fully-qualified reader macro syntax to assume the name ends in #, so we don't have to repeat it with an escape, with an overhead of only a few characters: ., !, and maybe a space. (Not that fully-qualified reader macros were ever short.)

@gilch
Copy link
Owner Author

gilch commented Sep 25, 2022

One illustration of why the current inject isn't sufficient, and why we'd need an as-reader-macro of some kind as a replacement, is that some objects are not pickleable.

#> .#(lambda :)
>>> # CompileError

(>   >  > >><function <lambda> at 0x000002229386C9D0><< <  <   <)
# Compiler.pickle() PicklingError:
#  Can't pickle <function <lambda> at 0x000002229386C9D0>: attribute lookup <lambda> on
 __main__ failed

#> 'builtins..repr#.#(lambda :) ; This works, but we're about to remove this syntax.
>>> '<function <lambda> at 0x000002229386C8B0>'
'<function <lambda> at 0x000002229386C8B0>'

#> '.#(repr '.#(lambda :)) ; The obvious direct alternative fails.
>>> # CompileError

repr(
  (>   >  > >><function <lambda> at 0x000002229386C4C0><< <  <   <)
  # Compiler.pickle() PicklingError:
  #  Can't pickle <function <lambda> at 0x000002229386C4C0>: attribute lookup <lambda> 
on __main__ failed
  )

#> 'B#!repr .#(lambda :) ; But an alias can do it!
>>> '<function <lambda> at 0x000002229386E0D0>'
'<function <lambda> at 0x000002229386E0D0>'

@gilch
Copy link
Owner Author

gilch commented Sep 26, 2022

Aliases already check for _macro_ in the module name and only append the QzHASH_ before lookup in that case. Fully-qualified reader macros could be made to work the same way. This is probably simpler than adding extras to Inject.

The one case this can't handle is when you want to use a compiler macro (living in a _macro_ namespace, but named without the trailing #) as a reader macro. Aliases have the same issue, but we could at least fully-qualify it as a workaround now. If fully-qualified macros were made to work the same way, we'd lose that workaround. Inject is still available as a workaround, but again, may have to go through pickles, and not everything is pickleable. The final workaround is to assign it a new name. Not ideal, but seems pretty rare. This is all that escaped hash is buying us. It hardly seems worth it.

@gilch gilch added the design Notes and ruminations that might lead somewhere label May 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design Notes and ruminations that might lead somewhere
Projects
None yet
Development

No branches or pull requests

1 participant