2016-06-05 4 views
2

Ich bin ein Anfänger in Racket sowie Lisp und spielte mit Syntaxdefinitionen. Ich definiert eine einfache Transformation wie folgt aus:Unterschied zwischen definieren und lassen w.r.t. syntax-rules keywords

(define-syntax hello 
    (syntax-rules (in) 
    ((_ name in world) (format "Hello ~a in ~a" name world)) 
    ((_ in name) (format "Hello ~a in here" name)))) 

Nun, wenn ich es wie diese beiden Fälle laufen, es funktioniert gut:

(hello "me" in "world") === "Hello me in world" 

Und

(define in "inside") 
(hello me in in) === "Hello me in inside" 

Aber diese Ergebnisse in einem Fehler

(let ([in "Shire"]) 
    (hello "Martin" in in)) === Error: hello: bad syntax in: (hello "Martin" in in) 

Also, Was ist der Grund für Hallo, hier in der Let-Bindung zu versagen, während es für eine Definition gut funktioniert? Wo kann ich genauere Informationen zu diesem Unterschied erhalten? Vielen Dank.

+1

BTW, ich sehe nur Ihre Frage auf #racket im IRC. Sie sollten definitiv länger als 2 Minuten bleiben. IRC ist meist ein lurk-orientiertes Medium; Sie erhalten die besten Ergebnisse, wenn Sie nur die eigentliche Frage posten (ohne auf jemanden warten zu müssen, um auf eine Antwort "Kann jemand helfen?" zu antworten) und dann 24 Stunden auf eine Antwort warten. :-) (Aber, wie du sehen kannst, konnten sowohl lexi-lambda als auch ich hier antworten, so dass es schließlich funktionierte.) –

Antwort

4

Dies hat mit der Art und Weise zu tun, wie Syntax-Literale arbeiten. Insbesondere gilt ein Syntaxliteral als übereinstimmend, wenn:

  • Das Literal hat keine Bindung zum Zeitpunkt der Makrodefinition und hat auch keine Bindung zum Zeitpunkt der Makroverwendung.
  • Das Literal hat eine Bindung zum Zeitpunkt der Makrodefinition, und auch hat die gleiche Bindung zum Zeitpunkt der Makroverwendung.
  • Der Fall funktionierte für Sie, weil Sie wahrscheinlich die in das gleiche Modul wie das Makro gesetzt haben. Das bedeutet, dass Kriterium 2 übereinstimmte: in hat die gleiche Bindung sowohl bei der Makrodefinition als auch bei der Verwendung. Aber wenn Sie das Makro in einem Modul (ohne die define für in) definiert und die in in einem anderen Modul definiert haben, wo Sie das Makro verwenden, werden die Dinge nicht so gut funktionieren. :-)

    Der let Fall erstellt eine neue Bindung für in. Dies entspricht niemals der Bindung für in bei der Makrodefinition auf oberster Ebene.

    +0

    Wenn ich also richtig verstanden habe, ist das 'let' wegen Kriterium 1 fehlgeschlagen, dh das Die Bindung für "in" ist während der Makroverwendung vorhanden, aber sie war nicht vorhanden, während das Makro definiert wurde. Aber ich bin immer noch nicht klar über die Definition, da diese Bindung auch nicht vorhanden war, während das Makro definiert wurde. Ich habe das Makro im Racket-Editor im Grunde definiert, ausgeführt und dann die 'in'-Bindung durch 'define' in der REPL definiert. Daher sollte das zweite Kriterium in diesem Fall nicht übereinstimmen. Oder fehlt mir etwas? –

    +0

    Und auch dieses Szenario tritt auf, wenn es eine globale Definition gibt. Wenn man innerhalb einer Funktion ein '(definiere n" etwas ")' hat und dann '(hallo" ok "in) aufruft, ergibt sich auch der gleiche Fehler. –

    +0

    Wie Alexis 'Antwort erklärt, beeinflussen 'Moduldefinitionen' alles im Modul ('gegenseitig rekursiv' war die Formulierung, die sie verwendete, aber immer noch dieselbe Bedeutung), nicht nur, was danach kommt. Wie für interne 'define' in einer Funktion, werden diese in 'letrec' umgewandelt (https://docs.racket-lang.org/guide/define.html#%28part._intdefs%29) und werden nicht behandelt genauso wie die Definition auf Modulebene. –

    3

    Der Grund dafür ist ein wenig subtile, weder „sollte“ Arbeit, aber wenn die define auf der obersten Ebene ist, ist es eigentlich Abspaltungen die Interpretation von syntax-rules, das ist, wo das Verhalten entsteht.

    Wenn Sie the documentation for syntax-rules schauen, sehen Sie, dass jeder literal-id die gleiche wie in syntax-case behandelt wird. Die Dokumentation stellt fest, dass die Literale werden miteinander verglichen unter Verwendung free-identifier=?:

    Ein id, die die gleichen Bindungs ​​als Ausgang literal-id eine Syntax Objekt übereinstimmt, der im Sinne der free-identifier=? eine Kennung mit der gleichen Bindung ist.

    Dies bedeutet, dass Literale sind Streichhölzer als Bindungen, nicht als Bezugspunkte.Das heißt, wenn Sie eine lokale Definition von in in einer let haben, ist es eine separate Bindung von in als Literal innerhalb syntax-rules markiert, so dass sie nicht übereinstimmen. Außerdem werden Kennungen, die mit in einem separaten Modul eingeführt wurden, ebenfalls nicht übereinstimmen.

    Interessanterweise wenn in aus dem Modul ausgeführt wird, die das Makro definiert, importierte dann rename-in verwenden, die noch umbenannt Bindung arbeiten, weil Racket Spur von Umbenennungen hält und hält sie für die gleiche Bindung sein, auch wenn sie nicht die gleicher Name. Das sind orthogonale Konzepte, auch wenn es manchmal leicht ist, alle Nuancen dieser Idee zu übersehen, aber es ist etwas, was der Hygiene innewohnt.

    Zurück zu dem fraglichen Beispiel, warum funktioniert das folgende?

    (define-syntax hello 
        (syntax-rules (in) 
        ((_ name in world) (format "Hello ~a in ~a" name world)) 
        ((_ in name) (format "Hello ~a in here" name)))) 
    
    (define in "inside") 
    (hello "me" in in) 
    

    Nun, Definition Kontexte sind gegenseitig rekursive, so die Definition von in nach der Makrodefinition wird von syntax-rules tatsächlich abgeholt. Daher erwartet das hello Makro tatsächlich die gleiche Bindung wie in, so dass der Aufruf funktioniert. Wenn jedoch wird hello von in in einem separaten Modul definiert, wird der Makroaufruf nicht Arbeit, weil die Bindungen seien anders:

    (module hello racket 
        (provide hello) 
        (define-syntax hello 
        (syntax-rules (in) 
         ((_ name in world) (format "Hello ~a in ~a" name world)) 
         ((_ in name) (format "Hello ~a in here" name))))) 
    
    (require 'hello) 
    
    (define in "inside") 
    (hello "me" in in) ; => hello: bad syntax in: (hello "me" in in) 
    
    +0

    Das erklärt es. Vielen Dank! –