unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
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


  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).