[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Proposal: Identifier properties for Guile
From: |
Daphne Preston-Kendal |
Subject: |
Proposal: Identifier properties for Guile |
Date: |
Tue, 10 Sep 2024 22:09:02 +0200 |
Hi!
I want to implement identifier properties a la SRFI 213 in Guile’s
psyntax.
<https://srfi.schemers.org/srfi-213/srfi-213.html>
These will be in R7RS Large (in the fascicle on macros coming out Real
Soon Now), and are in fact the only major macro feature Guile is
missing from R7 Large.
I want to gather feedback on my proposed strategy to implement these
into Guile before starting work. Along the way, I’ll explain some
nuances of the semantics which the SRFI explains very badly (the
fascicle on macros completely redid the text).
First of all, why would you want identifier properties? As an
admittedly pretty advanced macrologist, I’m convinced these are the
best things since sliced bread. I’ve gathered some example use cases
here:
<https://codeberg.org/scheme/r7rs/wiki/Identifier-property-use-cases>
and in fact the second use case is no longer a hypothetical as it was
when I wrote that page, because I have actually implemented it:
<https://codeberg.org/dpk/extensible-match>
Getting that library working in Guile and Hoot is my main personal
motivation for wanting this feature – apart from wanting to see an
implementation support the R7-large spec which I wrote ;-)
Identifier properties are fundamentally very similar to transformer
bindings, and pose some of the same challenges. The main difference is
that identifier properties are attached in addition to a binding,
rather than changing the regular Scheme semantics of the binding.
But like transformer bindings, it makes sense to divide them into
top-level (module-level, in Guile) property definitions and local
definitions.
The latter are easier to deal with because they don’t need to touch
the module system. The expander gets rid of them in the same way it
gets rid of let-syntax; expanded code doesn’t need to care about them
any more.
Implementing this means changing the expander’s idea of a wrap so that
it contains property bindings, much as has been done in Chez’s version
of psyntax:
<https://github.com/cisco/ChezScheme/blob/658e0b152abe3bc4ba889883d4dc218fef093aef/s/syntax.ss#L892>
This is a non-obtrusive change entirely localized to the expander as
it exists in a live Guile system. I don’t see any major issues here or
have any questions.
When we get to module-level property bindings, things get trickier.
I’d like a bit of guidance here. First, to clarify the SRFI:
identifier properties, as the name suggests, are attached to
identifiers in the sense of define/import/export. If you import an
identifier from module A which the unrelated module B has attached a
property to, you won’t see that property – unless you *also* re-import
the same identifier from module B. Identifiers are organized, though,
by keys, which themselves look like identifiers but those identifiers
are actually used only for their bindings in the sanse of
‘free-identifier=?’. (Keys have to be defined – the fallback to
symbolic comparison doesn’t apply here.)
To support this I will have to extend the definition of a module in
boot-9.scm to add, effectively, a second obarray – this one mapping
variables to mappings of bindings to property values, rather than
variables to values directly. However, it looks to me like there is
some duplicate definition between C and Scheme here, and I would also
need to update libguile/modules.c to at least be aware of the new
structure, and probably write the property mapping code in C there as
well to make it work as I describe below. Is this a correct
assumption?
Looking at how syntax transformers work in the current code, the
‘define-property’ form will probably have to be considered primitive
down to a fairly low level. As I understand it from looking at
expansion and disassembly, (define-syntax x y) first expands into
(define x (make-syntax-transformer something something y)), then on
the way from Tree-IL to bytecode it becomes an intrinsic call which is
effectively (define! (current-module) 'x) followed by a (set! 'x
(make-syntax-transformer something something y)). It looks like there
might be double definition here too: once in a direct route from
Tree-IL to bytecode, another with the same source and target but via
CPS.
In any case, based on this, I will add a <toplevel-define-property>
Tree-IL node which compiles to a new define-property! intrinsic the
same way <toplevel-define> compiles to a define!. That primitive will
add the var -> binding -> value mapping to the module. Since there is
no set! operation on properties, I think everything can be done in one
intrinsic. One minor question I will have to solve is how to represent
the binding used for the key down at this level. I am sure there is a
simple answer to this – possibly just a pair:
(<original-module> . <var-name>)
There is an alternative approach which looks easier, which would be to
have define-property at the top level expand directly into a call to a
new ‘module-define-property!’ procedure. But I assume there is a
reason macro transformers and other definitions were done the way they
are done …
Also, because this reaches so far down into the internals of Guile, I
assume it will be necessary to adapt some of this specially for Hoot
as well. I haven’t looked at the Hoot sources yet – it might be an
idea to wait until Andy Wingo has finished porting psyntax to Hoot
before trying anything there.
In any case, there is then one last step, which is to add code
adding/merging properties when modules are imported into other
modules. This should be a fairly simple hash table/alist/whatever
union operation, but I haven’t looked into it yet.
Since this does touch nearly every level of Guile’s compiler, I would
appreciate some feedback and ideally the opportunity to consult
occasionally while I’m working with someone who is more familiar with
all of this code than I am.
Many thanks in advance,
--
dpk (Daphne Preston-Kendal) ·· 12103 Berlin, Germany ·· http://dpk.io/
One Thing to name them all, One Thing to define them,
One Thing to place them in environments and bind them,
In the Lambda Order they are all first class. — R2RS
- Proposal: Identifier properties for Guile,
Daphne Preston-Kendal <=