[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Macro for replacing a placeholder in an expression
Re: Macro for replacing a placeholder in an expression
Sat, 30 Jul 2022 15:42:33 +0000
On 7/28/22 12:23, Zelphir Kaltstahl wrote:
On 7/28/22 02:55, Maxime Devos wrote:
I'll need to look at this and learn about eval-when. I also did not think
about vectors yet. Thank you!
These macros all sound more complicated than necessary -- on the first one,
I've sent you a message with sneek:
;; By: Maxime Devos
;; This does not recurse into #(...).
;; Also, such a construct does not nest well, you can't put a
replace-result-placeholder inside a replace-result-placeholder meaningfully,
;; so I'm wondering why you're doing this, maybe your goal can be
accomplished more robustly with a different method.
(eval-when (expand load eval)
(define (replace-placeholder new code) ; <--- recursively transforms code
to replace '<?>' by new
(syntax-case code (<?>)
((x . y)
#`(#,(replace-placeholder new #'x) . #,(replace-placeholder new #'y)))
(syntax-case s (<?>) ; <?>: placeholder
((_ new code) (replace-placeholder #'new #'code)))))
(<?> bar))) ; -> bar
(I think thinking in terms of 'operations' and special-casing lambda etc
would make things harder here)
As a bonus, this supports things like `((x . <?>) (z . w)) which aren't
supported by the original macro as that macro assumed lists.
I've now tried to use syntax-case, trying to adapt your example to what I need.
However, it seems again I am stuck.
From the docs I read that syntax-case needs to be wrapped into a lambda,
because it is just a way of working with syntax objects, pattern matching on
them, but it does not make a syntax transformer. To make an actual syntax
transformer, it needs to be wrapped with a lambda. So far I understand it. It is
like a normal (match ...), but for syntax:
"All of these differences stem from the fact that syntax-case does not define a
syntax transformer itself – instead, syntax-case expressions provide a way to
destructure a syntax object, and to rebuild syntax objects as output." --
OK fine. But in the manual the syntax-case is used with define-syntax, not with
define, like in your example. I guess that is the difference between using it as
part of a macro and using it as a helper in a function:
"It is not strictly necessary for a syntax-case expression to return a syntax
object, because syntax-case expressions can be used in helper functions, or
otherwise used outside of syntax expansion itself. However a syntax transformer
procedure must return a syntax object, so most uses of syntax-case do end up
returning syntax objects." --
I struggled a bit to bring arguments of the wrapping lambda in correspondence
with the patterns I supply to the pattern matching in syntax-case, but now I
understand, the lambda always has only one argument, if used inside a
define-syntax and that one argument is the whole call, while in your example you
used syntax-case inside a regular function, so the arguments are whatever you
want to define them to be. So that I understand now.
But now comes the problem:
Since I want to replace all occurrences of for example <?> and <?> does not need
to be defined, I think I must use define-syntax, to avoid Guile trying to
evaluate the arguments to a function call. OK, so a macro I write:
(syntax-case stx (<?>)
[(_ replacement <?>)
[(_ replacement (car-elem . cdr-elem))
((unsyntax (replace-placeholder #'replacement #'car-elem)) .
(unsyntax (replace-placeholder #'replacement #'cdr-elem))))]
[(_ replacement other)
(I am still a bit not used to all the # shortcuts for (syntax …), (quasisyntax
…) and (unsyntax …), so I wrote them out as words for now.)
When I use this on a trivial expression, it works:
(replace-placeholder 3 <?>)
When I try to use this for a pair as follows:
(replace-placeholder 3 (+ 1 <?>))
=> While compiling expression:
Wrong type to apply: #<syntax-transformer replace-placeholder>
It does not work. What happens here, I guess, is, that the macro gets expanded,
then the syntax-transformer ends up in a place like (replace-placeholder …) and
since it is not a function, it cannot be applied. But this is exactly what I
want! I want Guile to do another macro call right there and replace in the
sub-expression. How can I tell Guile to do that?
I think that only now I am understanding properly what you wrote: "Also, such a
construct does not nest well, you can't put a replace-result-placeholder inside
a replace-result-placeholder meaningfully, […]". Does this mean, that recursive
application of a macro inside a macro is impossible? To expand to subforms being
the same macro again and this way transform a whole tree of s-expressions?
"All I want to do" is to replace some placeholder (in this case <?>) in an
arbitrary form. No matter how that form looks or how deeply it is nested, if
there are <?> inside of it, I want to replace them. Is this impossible?
Ultimately this is a sub-problem of a bigger thing I want to do. Part of the
contracts thingy. I want to make it so, that the following is valid and works:
(require (<= amount account-balance)
(>= amount 0))
(ensure (>= <?> 0)
(λ (amount account-balance)
(- account-balance amount)))
In SRFI 197 someone seems to have done that:
https://srfi.schemers.org/srfi-197/srfi-197.html The underscore _ can be
anywhere and the result of previous chain steps will be put there.
Perhaps I have to check how that is implemented.
Re: Macro for replacing a placeholder in an expression, Maxime Devos, 2022/07/27