From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Zelphir Kaltstahl Newsgroups: gmane.lisp.guile.user Subject: Re: Macro for replacing a placeholder in an expression Date: Sat, 30 Jul 2022 15:42:33 +0000 Message-ID: <5f0efafc-3045-e710-6066-9519692ebb44@posteo.de> References: <80083c86-19b0-7537-be70-f763bd5b390f@posteo.de> <1ede5481-9d46-84e9-2530-d64ac835568b@telenet.be> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="1768"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Guile User To: Maxime Devos Original-X-From: guile-user-bounces+guile-user=m.gmane-mx.org@gnu.org Sat Jul 30 17:42:56 2022 Return-path: Envelope-to: guile-user@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1oHocC-0000Cv-65 for guile-user@m.gmane-mx.org; Sat, 30 Jul 2022 17:42:56 +0200 Original-Received: from localhost ([::1]:37172 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1oHocA-0007tF-Im for guile-user@m.gmane-mx.org; Sat, 30 Jul 2022 11:42:54 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:56344) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oHobx-0007t5-0K for guile-user@gnu.org; Sat, 30 Jul 2022 11:42:41 -0400 Original-Received: from mout01.posteo.de ([185.67.36.65]:55445) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oHobu-0005w7-89 for guile-user@gnu.org; Sat, 30 Jul 2022 11:42:40 -0400 Original-Received: from submission (posteo.de [185.67.36.169]) by mout01.posteo.de (Postfix) with ESMTPS id D9A86240027 for ; Sat, 30 Jul 2022 17:42:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.de; s=2017; t=1659195754; bh=c3ytyUODJ30C+bg93q0ptDDdQGuN8ADAAL5tWg49rc4=; h=Date:Subject:From:To:Cc:From; b=OU23+xNDcmOO2IgQkA0jWyMHHgFnTOyIq6SjXrwCzbWLolwVmlVH2q9YpX5p9j8DE UcqzGW32+eFpRcQJ3Q3RjBQjHKknFPEuOB9B9sM7mhsUcH5zDJWFZpvyn57Boej+Ao colf9Bn9SwK4EjlFcCzN8r3cDrNPcruDafvI/wgKRnESV5HhJJTn/InL8onv9ZDO07 VaA6hZfjYUVD1MuxYEVqITvyDrnZntZwH27b/u/ydbSsnK/hAhjHhp7S9b/kBDEVQq vQXMWLPm4D33eujHhT/CqFd0VbBhtDjCCpqljv2oOFsndF/sKIiCRKtqRQuCfAtrDY a1TUjSa4gzp2Q== Original-Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4Lw7td6LvSz6tpg; Sat, 30 Jul 2022 17:42:33 +0200 (CEST) Content-Language: en-US In-Reply-To: Received-SPF: pass client-ip=185.67.36.65; envelope-from=zelphirkaltstahl@posteo.de; helo=mout01.posteo.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-Content-Filtered-By: Mailman/MimeDel 2.1.29 X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: General Guile related discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-user-bounces+guile-user=m.gmane-mx.org@gnu.org Original-Sender: "guile-user" Xref: news.gmane.io gmane.lisp.guile.user:18465 Archived-At: 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: # ~~~~ 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