From: Zelphir Kaltstahl <zelphirkaltstahl@posteo.de>
To: Maxime Devos <maximedevos@telenet.be>
Cc: Guile User <guile-user@gnu.org>
Subject: Re: Macro for replacing a placeholder in an expression
Date: Sat, 30 Jul 2022 15:42:33 +0000 [thread overview]
Message-ID: <5f0efafc-3045-e710-6066-9519692ebb44@posteo.de> (raw)
In-Reply-To: <a2ea28e1-69f9-5fc4-e856-2f9b8e51f7a9@posteo.de>
Hi Maxime!
On 7/28/22 12:23, Zelphir Kaltstahl wrote:
> Hello Maxime!
>
> On 7/28/22 02:55, Maxime Devos wrote:
>>
>> 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 (<?>)
>> (<?> new)
>> ((x . y)
>> #`(#,(replace-placeholder new #'x) . #,(replace-placeholder new #'y)))
>> (rest #'rest))))
>>
>> (define-syntax replace-result-placeholder
>> (lambda (s)
>> (syntax-case s (<?>) ; <?>: placeholder
>> ((_ new code) (replace-placeholder #'new #'code)))))
>>
>> (display (replace-result-placeholder
>> quote
>> (<?> 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.
>>
>> Greetings,
>> Maxime.
>>
> I'll need to look at this and learn about eval-when. I also did not think
> about vectors yet. Thank you!
>
> Best regards,
> Zelphir
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." --
https://www.gnu.org/software/guile/manual/html_node/Syntax-Case.html
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." --
https://www.gnu.org/software/guile/manual/html_node/Syntax-Case.html
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:
~~~~
(define-syntax replace-placeholder
(λ (stx)
(syntax-case stx (<?>)
[(_ replacement <?>)
(syntax replacement)]
[(_ replacement (car-elem . cdr-elem))
(quasisyntax
((unsyntax (replace-placeholder #'replacement #'car-elem)) .
(unsyntax (replace-placeholder #'replacement #'cdr-elem))))]
[(_ replacement other)
(syntax 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 <?>)
=> 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:
~~~~
(define-with-contract account-withdraw
(require (<= amount account-balance)
(>= amount 0))
(ensure (>= <?> 0)
arbitrarily-complex-expression-here-where-placeholder-will-be-replaced-with-function-result-identifier)
(λ (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.
Best regards,
Zelphir
--
repositories:https://notabug.org/ZelphirKaltstahl
next prev parent reply other threads:[~2022-07-30 15:42 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-07-27 23:57 Macro for replacing a placeholder in an expression Zelphir Kaltstahl
2022-07-28 0:55 ` Maxime Devos
2022-07-28 10:23 ` Zelphir Kaltstahl
2022-07-28 10:28 ` Maxime Devos
2022-07-30 15:42 ` Zelphir Kaltstahl [this message]
2022-07-30 20:44 ` Maxime Devos
2022-07-30 21:10 ` Maxime Devos
2022-07-30 21:13 ` Maxime Devos
2022-08-05 9:42 ` Linus Björnstam
2022-08-06 14:28 ` Zelphir Kaltstahl
2022-07-28 1:04 ` Maxime Devos
2022-07-28 8:39 ` Zelphir Kaltstahl
2022-07-28 9:48 ` Maxime Devos
2022-07-28 11:26 ` Zelphir Kaltstahl
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/guile/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5f0efafc-3045-e710-6066-9519692ebb44@posteo.de \
--to=zelphirkaltstahl@posteo.de \
--cc=guile-user@gnu.org \
--cc=maximedevos@telenet.be \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).