From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Taylan Kammer Newsgroups: gmane.lisp.guile.user Subject: Re: return macro Date: Mon, 28 Jun 2021 03:15:34 +0200 Message-ID: <1142492b-16bf-7181-8d93-38bfc2d3c748@gmail.com> References: Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="2264"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Thunderbird/78.11.0 To: Damien Mattei , guile-user Original-X-From: guile-user-bounces+guile-user=m.gmane-mx.org@gnu.org Mon Jun 28 03:15:53 2021 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 1lxfsP-0000Jz-1r for guile-user@m.gmane-mx.org; Mon, 28 Jun 2021 03:15:53 +0200 Original-Received: from localhost ([::1]:38464 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lxfsN-0005wI-RL for guile-user@m.gmane-mx.org; Sun, 27 Jun 2021 21:15:51 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:34268) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lxfsC-0005vv-Eq for guile-user@gnu.org; Sun, 27 Jun 2021 21:15:40 -0400 Original-Received: from mail-ej1-x634.google.com ([2a00:1450:4864:20::634]:39694) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1lxfsA-0000g0-Lr for guile-user@gnu.org; Sun, 27 Jun 2021 21:15:40 -0400 Original-Received: by mail-ej1-x634.google.com with SMTP id yy20so18976866ejb.6 for ; Sun, 27 Jun 2021 18:15:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:to:references:from:message-id:date:user-agent:mime-version :in-reply-to:content-language:content-transfer-encoding; bh=qF7fRaUzTQND1Or6hmZ8aVPyVgE+6B2+I5kRO15Qq1s=; b=Gs7tS7+5dQkrQjpbkebjw6Y2QuMv01Ma5QJ+BMCOU6VSGNfP0vi4HXf9tNJlwuwF7E l0Ar3khHhKXPmi3lmqrbvHj+CGyrxpD5YfvyG/QTKQ9KS3CyVQDI1MXQD7oNPqRUPNlb jTg085f07NS+xmXS7x4mphbijDt5MZGOusXe9rEpv/ImQ3IOEoawowaNuwcAfouoPsOc ww0pyPQt1/YQSJAbDMs3+pgdhLBKlhKpfrhhe4ZlIqXvO0FR17ErnKo4QCfkpoG4V9/z W9FLGEGB1mvfVm9Fv9AiQGG4kEpA0Aqu/ePXjbijC1UKbfKtVOcjfSXlufjof12rlLXD 1zKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=qF7fRaUzTQND1Or6hmZ8aVPyVgE+6B2+I5kRO15Qq1s=; b=G5Q23hFglPLD+8VowhROjN9KJU5v6dKQ4rX8Mo71ro3wV9pbMQzBLL1n9kKhYUfIa3 Ltli3dSVS4dthCFP5M3m7L1v+a6+ZLYrOf7CscWCdvGmRW90NAL8MQ2Q1x/PG7FLatth BFVkas6TqZFYdiOgDwyBuFqZb3PSCD6Yvi8d1Fs7w2+mLbVc2ufFgQmmSxdF6IxpnG+M kU9B/++ocoBCpzdotDtcR/J6vyyEkWdAXbjrfIsuYyL+DES/ZhQQRNsD0M0+nZywqOZq EN6YVWgFcdGRZCZM6qCwtU2FDdpTjzM5PezFQ5zkcsGOR1lV5ztUGZ8dI5659YQiG1q5 uvPA== X-Gm-Message-State: AOAM531YSEn1qBH13Cua/z1j0qOS64vo7rfnESNLAY+cgNt1oPiMK91Z NMjYlxqioHxpyiQOx9KJ5JOqhkOHzaEYzA== X-Google-Smtp-Source: ABdhPJw+57SYvGkvZLmMJszB7tr3uqO9yAn9nO0jY4BJPAtE5kEDK3ZNxlnLM9CJ6IZm8iWxPLHAJw== X-Received: by 2002:a17:906:8584:: with SMTP id v4mr18436085ejx.443.1624842935894; Sun, 27 Jun 2021 18:15:35 -0700 (PDT) Original-Received: from [192.168.178.20] (b2b-109-90-125-150.unitymedia.biz. [109.90.125.150]) by smtp.gmail.com with ESMTPSA id hz11sm6181191ejc.125.2021.06.27.18.15.35 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sun, 27 Jun 2021 18:15:35 -0700 (PDT) In-Reply-To: Content-Language: en-US Received-SPF: pass client-ip=2a00:1450:4864:20::634; envelope-from=taylan.kammer@gmail.com; helo=mail-ej1-x634.google.com X-Spam_score_int: -38 X-Spam_score: -3.9 X-Spam_bar: --- X-Spam_report: (-3.9 / 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, FREEMAIL_FROM=0.001, NICE_REPLY_A=-1.765, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.23 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:17621 Archived-At: On 28.06.2021 01:10, Damien Mattei wrote: > hi, > > i wanted to create a macro that is used like a function definition and > allow return using call/cc: > > (define-syntax def > (syntax-rules (return) > ((_ (name args ...) body body* ...) > (define name (lambda (args ...) > (call/cc (lambda (return) body body* ...))))) > ((_ name expr) (define name expr)))) > > unfortunaly i got error: > > scheme@(guile-user)> (def (test x) (cond ((= x 3) 7) ((= x 2) (return 5)) > (else 3))) > ;;; :2:42: warning: possibly unbound variable `return' > scheme@(guile-user)> (test 2) > ice-9/boot-9.scm:1685:16: In procedure raise-exception: > Unbound variable: return > > > any idea? Hi Damien, This is because of the "hygiene" rule of Scheme, where the notion of "lexical scope" is taken very seriously: for an identifier to be bound, it must be visible in the lexical (textual) surroundings where it has been bound. So for instance, in the following example code: (def (test x) (cond ((= x 3) (return 7)) ((= x 2) (return 5)))) We can't see a binding for "return" anywhere in the text, therefore it cannot be bound. This is good "default" behavior because it makes code more flexible and easier to understand. An easy way of overcoming this issue is to let the user explicitly name the return identifier however they like: (define-syntax def (syntax-rules () ((_ (name ret arg ...) body body* ...) (define (name arg ...) (call/cc (lambda (ret) body body* ...)))))) Now you could define: (def (test return x) (cond ((= x 3) (return 7)) ((= x 2) (return 5)))) Or for instance: (def (test blubba x) (cond ((= x 3) (blubba 7)) ((= x 2) (blubba 5)))) However, sometimes you're sure that you want to make an implicit binding for an identifier, and for those cases you need to write an "unhygienic" macro which can be achieved with the more complex macro system "syntax-case". Here's how your desired macro could be defined. I will use identifiers like "" just for easier readability; they don't have any special meaning: (define-syntax def (lambda (stx) (syntax-case stx () ((_ ( ...) * ...) (let ((ret-id (datum->syntax stx 'return))) #`(define ( ...) (call/cc (lambda (#,ret-id) * ...)))))))) There's a few things here to take note of: - Unlike with syntax-rules, the syntax-case is contained in a lambda which takes a single argument: a "syntax object" which is passed to syntax-case - Unlike with syntax-rules, the "body" of the macro (where it begins with a 'let') is not immediately part of the generated code; that 'let' is actually executed during compile-time. The body of the macro must result in an object of the type "syntax object" that represents the generated code. - You see that I define a variable called "ret-id" which I bind to the result of the expression: (datum->syntax stx 'return) which means "create a syntax object in the same lexical environment as stx, and is represented by the symbol 'return'." - The actual code generation begins within the #`(...) which is a shorthand for (quasisyntax (...)) just like '(...) is short for (quote (...)). The result of a quasisyntax expression is a syntax object. Basically, it's the most convenient way of creating a syntax object, but like syntax-rules it's also hygienic by default and you need to insert "unhygienic" syntax objects into it explicitly. - Within the quasisyntax, I use #,ret-id which is short for (unsyntax ret-id) to inject the unhygienic syntax object that holds the symbol 'return' into the generated code. For someone used to macros in the Common Lisp or Elisp style, this may seem over-complicated. It's the cost of the "hygienic by default" behavior. By the way I assume that you're just toying around with the language to learn. If you were thinking of using a 'def' macro like this in real code, I would discourage it because there's already a built-in mechanism that allows the programmer something very similar, called 'let/ec': (import (ice-9 control)) (define (test x) (let/ec return (cond ((= x 3) (return 7)) ((= x 2) (return 5))))) As you see it allows you to define a "return" keyword anywhere, and it doesn't need to be part of a function definition, it can appear anywhere. -- Taylan