[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Macro for replacing a placeholder in an expression

From: Zelphir Kaltstahl
Subject: Re: Macro for replacing a placeholder in an expression
Date: Sat, 30 Jul 2022 15:42:33 +0000

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
            (<?> 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'll need to look at this and learn about eval-when. I also did not think about vectors yet. Thank you!

Best regards,

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:

(define-syntax replace-placeholder
  (λ (stx)
    (syntax-case stx (<?>)
      [(_ replacement <?>)
       (syntax replacement)]
      [(_ replacement (car-elem . cdr-elem))
        ((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)
  (λ (amount account-balance)
    (- account-balance amount)))

In SRFI 197 someone seems to have done that: 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,


reply via email to

[Prev in Thread] Current Thread [Next in Thread]