how can I compare syntax objects in racket?












4














I'd like to compare the code contents of two syntax objects and ignore things like contexts. Is converting them to datum the only way to do so? Like:



(equal? (syntax->datum #'(x+1)) (syntax->datum #'(x+1)))









share|improve this question



























    4














    I'd like to compare the code contents of two syntax objects and ignore things like contexts. Is converting them to datum the only way to do so? Like:



    (equal? (syntax->datum #'(x+1)) (syntax->datum #'(x+1)))









    share|improve this question

























      4












      4








      4







      I'd like to compare the code contents of two syntax objects and ignore things like contexts. Is converting them to datum the only way to do so? Like:



      (equal? (syntax->datum #'(x+1)) (syntax->datum #'(x+1)))









      share|improve this question













      I'd like to compare the code contents of two syntax objects and ignore things like contexts. Is converting them to datum the only way to do so? Like:



      (equal? (syntax->datum #'(x+1)) (syntax->datum #'(x+1)))






      racket






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 22 at 20:34









      JRR

      2,19332445




      2,19332445
























          2 Answers
          2






          active

          oldest

          votes


















          4














          If you want to compare both objects without deconstructing them at all, then yes.



          HOWEVER, the problem with this method is that it only compares the datum attached to two syntax objects, and won't actually compare their binding information.



          The analogy that I've heard (from Ryan Culpepper), is this is kind of like taking two paintings, draining of them of their color, and seeing if they are identical. While they might be similar in some ways, you will miss a lot of differences from the different colors.



          A better approach (although it does require some work), is to use syntax-e to destruct the syntax object into more primitive lists of syntax objects, and do this until you get identifiers (basically a syntax object whose datum is a symbol), from there, you can generally use free-identifier=? (and sometimes bound-identifier=? to see if each identifier can bind each other, and identifier-binding to compare module level identifiers.



          The reason why there isn't a single simple predicate to compare two arbitrary syntax objects is because, generally, there isn't really one good definition for what makes two pieces of code equal, even if you only care about syntactic equality. For example, using the functions referenced above doesn't track internal bindings in a syntax object, so you will still get a very strict definition of what it means to be 'equal'. that is, both syntax objects have the same structure with identifiers that are either bound to the same module, or are free-identifier=?.



          As such, before you use this answer, I highly recommend you take a step back and make sure this is really what you want to do. Once in a blue moon it is, but most of the time you actually are trying to solve a similar, yet simpler, problem.






          share|improve this answer





























            1














            Here's a concrete example of one possible way you could do the "better approach" Leif Andersen mentioned.



            I have used this in multiple places for testing purposes, though if anyone wanted to use it in non-test code, they would probably want to re-visit some of the design decisions.



            However, things like the equal?/recur pattern used here should be helpful no matter how you decide to define what equality means.



            Some of the decisions you might want to make different choices on:




            • On identifiers, do you want to check that the scopes are exactly the same (bound-identifier=?), or would you want to assume that they would be bound outside of the syntax object and check that they are bound to the same thing, even if they have different scopes (free-identifier=?)? Note that if you choose the first one, then checking the results of macro expansion will sometimes return #false because of scope differences, but if you choose the second one, then if any identifier is not bound outside of the syntax object, then it would be as if you only care about symbol=? equality on names, so it will return #true in some places where it shouldn't. I chose the first one bound-identifier=? here because for testing, a "false positive" where the test fails is better than a "false negative" where the tests succeeds in cases it shouldn't.


            • On source locations, do you want to check that they are equal, or do you want to ignore them? This code ignores them because it's only for testing purposes, but if you want equality only for things which have the same source location, you might want to check that using functions like build-source-location-list.


            • On syntax properties, do you want to check that they are equal, or do you want to ignore them? This code ignores them because it's only for testing purposes, but if you want to check that you might would to check that using functions like syntax-property-symbol-keys.



            Finally here is the code. It may not be exactly what you want depending on how you answered the questions above. However, its structure and how it uses equal?/recur might be helpful to you.



            (require rackunit)

            ;; Works on fully wrapped, non-wrapped, and partially
            ;; wrapped values, and it checks that the the inputs
            ;; are wrapped in all the same places. It checks scopes,
            ;; but it does not check source location.
            (define-binary-check (check-stx=? stx=? actual expected))

            ;; Stx Stx -> Bool
            (define (stx=? a b)
            (cond
            [(and (identifier? a) (identifier? b))
            (bound-identifier=? a b)]
            [(and (syntax? a) (syntax? b))
            (and (bound-identifier=? (datum->syntax a '||) (datum->syntax b '||))
            (stx=? (syntax-e a) (syntax-e b)))]
            [else
            (equal?/recur a b stx=?)]))





            share|improve this answer





















              Your Answer






              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "1"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: true,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: 10,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














              draft saved

              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53437701%2fhow-can-i-compare-syntax-objects-in-racket%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              4














              If you want to compare both objects without deconstructing them at all, then yes.



              HOWEVER, the problem with this method is that it only compares the datum attached to two syntax objects, and won't actually compare their binding information.



              The analogy that I've heard (from Ryan Culpepper), is this is kind of like taking two paintings, draining of them of their color, and seeing if they are identical. While they might be similar in some ways, you will miss a lot of differences from the different colors.



              A better approach (although it does require some work), is to use syntax-e to destruct the syntax object into more primitive lists of syntax objects, and do this until you get identifiers (basically a syntax object whose datum is a symbol), from there, you can generally use free-identifier=? (and sometimes bound-identifier=? to see if each identifier can bind each other, and identifier-binding to compare module level identifiers.



              The reason why there isn't a single simple predicate to compare two arbitrary syntax objects is because, generally, there isn't really one good definition for what makes two pieces of code equal, even if you only care about syntactic equality. For example, using the functions referenced above doesn't track internal bindings in a syntax object, so you will still get a very strict definition of what it means to be 'equal'. that is, both syntax objects have the same structure with identifiers that are either bound to the same module, or are free-identifier=?.



              As such, before you use this answer, I highly recommend you take a step back and make sure this is really what you want to do. Once in a blue moon it is, but most of the time you actually are trying to solve a similar, yet simpler, problem.






              share|improve this answer


























                4














                If you want to compare both objects without deconstructing them at all, then yes.



                HOWEVER, the problem with this method is that it only compares the datum attached to two syntax objects, and won't actually compare their binding information.



                The analogy that I've heard (from Ryan Culpepper), is this is kind of like taking two paintings, draining of them of their color, and seeing if they are identical. While they might be similar in some ways, you will miss a lot of differences from the different colors.



                A better approach (although it does require some work), is to use syntax-e to destruct the syntax object into more primitive lists of syntax objects, and do this until you get identifiers (basically a syntax object whose datum is a symbol), from there, you can generally use free-identifier=? (and sometimes bound-identifier=? to see if each identifier can bind each other, and identifier-binding to compare module level identifiers.



                The reason why there isn't a single simple predicate to compare two arbitrary syntax objects is because, generally, there isn't really one good definition for what makes two pieces of code equal, even if you only care about syntactic equality. For example, using the functions referenced above doesn't track internal bindings in a syntax object, so you will still get a very strict definition of what it means to be 'equal'. that is, both syntax objects have the same structure with identifiers that are either bound to the same module, or are free-identifier=?.



                As such, before you use this answer, I highly recommend you take a step back and make sure this is really what you want to do. Once in a blue moon it is, but most of the time you actually are trying to solve a similar, yet simpler, problem.






                share|improve this answer
























                  4












                  4








                  4






                  If you want to compare both objects without deconstructing them at all, then yes.



                  HOWEVER, the problem with this method is that it only compares the datum attached to two syntax objects, and won't actually compare their binding information.



                  The analogy that I've heard (from Ryan Culpepper), is this is kind of like taking two paintings, draining of them of their color, and seeing if they are identical. While they might be similar in some ways, you will miss a lot of differences from the different colors.



                  A better approach (although it does require some work), is to use syntax-e to destruct the syntax object into more primitive lists of syntax objects, and do this until you get identifiers (basically a syntax object whose datum is a symbol), from there, you can generally use free-identifier=? (and sometimes bound-identifier=? to see if each identifier can bind each other, and identifier-binding to compare module level identifiers.



                  The reason why there isn't a single simple predicate to compare two arbitrary syntax objects is because, generally, there isn't really one good definition for what makes two pieces of code equal, even if you only care about syntactic equality. For example, using the functions referenced above doesn't track internal bindings in a syntax object, so you will still get a very strict definition of what it means to be 'equal'. that is, both syntax objects have the same structure with identifiers that are either bound to the same module, or are free-identifier=?.



                  As such, before you use this answer, I highly recommend you take a step back and make sure this is really what you want to do. Once in a blue moon it is, but most of the time you actually are trying to solve a similar, yet simpler, problem.






                  share|improve this answer












                  If you want to compare both objects without deconstructing them at all, then yes.



                  HOWEVER, the problem with this method is that it only compares the datum attached to two syntax objects, and won't actually compare their binding information.



                  The analogy that I've heard (from Ryan Culpepper), is this is kind of like taking two paintings, draining of them of their color, and seeing if they are identical. While they might be similar in some ways, you will miss a lot of differences from the different colors.



                  A better approach (although it does require some work), is to use syntax-e to destruct the syntax object into more primitive lists of syntax objects, and do this until you get identifiers (basically a syntax object whose datum is a symbol), from there, you can generally use free-identifier=? (and sometimes bound-identifier=? to see if each identifier can bind each other, and identifier-binding to compare module level identifiers.



                  The reason why there isn't a single simple predicate to compare two arbitrary syntax objects is because, generally, there isn't really one good definition for what makes two pieces of code equal, even if you only care about syntactic equality. For example, using the functions referenced above doesn't track internal bindings in a syntax object, so you will still get a very strict definition of what it means to be 'equal'. that is, both syntax objects have the same structure with identifiers that are either bound to the same module, or are free-identifier=?.



                  As such, before you use this answer, I highly recommend you take a step back and make sure this is really what you want to do. Once in a blue moon it is, but most of the time you actually are trying to solve a similar, yet simpler, problem.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 22 at 22:13









                  Leif Andersen

                  11.2k135385




                  11.2k135385

























                      1














                      Here's a concrete example of one possible way you could do the "better approach" Leif Andersen mentioned.



                      I have used this in multiple places for testing purposes, though if anyone wanted to use it in non-test code, they would probably want to re-visit some of the design decisions.



                      However, things like the equal?/recur pattern used here should be helpful no matter how you decide to define what equality means.



                      Some of the decisions you might want to make different choices on:




                      • On identifiers, do you want to check that the scopes are exactly the same (bound-identifier=?), or would you want to assume that they would be bound outside of the syntax object and check that they are bound to the same thing, even if they have different scopes (free-identifier=?)? Note that if you choose the first one, then checking the results of macro expansion will sometimes return #false because of scope differences, but if you choose the second one, then if any identifier is not bound outside of the syntax object, then it would be as if you only care about symbol=? equality on names, so it will return #true in some places where it shouldn't. I chose the first one bound-identifier=? here because for testing, a "false positive" where the test fails is better than a "false negative" where the tests succeeds in cases it shouldn't.


                      • On source locations, do you want to check that they are equal, or do you want to ignore them? This code ignores them because it's only for testing purposes, but if you want equality only for things which have the same source location, you might want to check that using functions like build-source-location-list.


                      • On syntax properties, do you want to check that they are equal, or do you want to ignore them? This code ignores them because it's only for testing purposes, but if you want to check that you might would to check that using functions like syntax-property-symbol-keys.



                      Finally here is the code. It may not be exactly what you want depending on how you answered the questions above. However, its structure and how it uses equal?/recur might be helpful to you.



                      (require rackunit)

                      ;; Works on fully wrapped, non-wrapped, and partially
                      ;; wrapped values, and it checks that the the inputs
                      ;; are wrapped in all the same places. It checks scopes,
                      ;; but it does not check source location.
                      (define-binary-check (check-stx=? stx=? actual expected))

                      ;; Stx Stx -> Bool
                      (define (stx=? a b)
                      (cond
                      [(and (identifier? a) (identifier? b))
                      (bound-identifier=? a b)]
                      [(and (syntax? a) (syntax? b))
                      (and (bound-identifier=? (datum->syntax a '||) (datum->syntax b '||))
                      (stx=? (syntax-e a) (syntax-e b)))]
                      [else
                      (equal?/recur a b stx=?)]))





                      share|improve this answer


























                        1














                        Here's a concrete example of one possible way you could do the "better approach" Leif Andersen mentioned.



                        I have used this in multiple places for testing purposes, though if anyone wanted to use it in non-test code, they would probably want to re-visit some of the design decisions.



                        However, things like the equal?/recur pattern used here should be helpful no matter how you decide to define what equality means.



                        Some of the decisions you might want to make different choices on:




                        • On identifiers, do you want to check that the scopes are exactly the same (bound-identifier=?), or would you want to assume that they would be bound outside of the syntax object and check that they are bound to the same thing, even if they have different scopes (free-identifier=?)? Note that if you choose the first one, then checking the results of macro expansion will sometimes return #false because of scope differences, but if you choose the second one, then if any identifier is not bound outside of the syntax object, then it would be as if you only care about symbol=? equality on names, so it will return #true in some places where it shouldn't. I chose the first one bound-identifier=? here because for testing, a "false positive" where the test fails is better than a "false negative" where the tests succeeds in cases it shouldn't.


                        • On source locations, do you want to check that they are equal, or do you want to ignore them? This code ignores them because it's only for testing purposes, but if you want equality only for things which have the same source location, you might want to check that using functions like build-source-location-list.


                        • On syntax properties, do you want to check that they are equal, or do you want to ignore them? This code ignores them because it's only for testing purposes, but if you want to check that you might would to check that using functions like syntax-property-symbol-keys.



                        Finally here is the code. It may not be exactly what you want depending on how you answered the questions above. However, its structure and how it uses equal?/recur might be helpful to you.



                        (require rackunit)

                        ;; Works on fully wrapped, non-wrapped, and partially
                        ;; wrapped values, and it checks that the the inputs
                        ;; are wrapped in all the same places. It checks scopes,
                        ;; but it does not check source location.
                        (define-binary-check (check-stx=? stx=? actual expected))

                        ;; Stx Stx -> Bool
                        (define (stx=? a b)
                        (cond
                        [(and (identifier? a) (identifier? b))
                        (bound-identifier=? a b)]
                        [(and (syntax? a) (syntax? b))
                        (and (bound-identifier=? (datum->syntax a '||) (datum->syntax b '||))
                        (stx=? (syntax-e a) (syntax-e b)))]
                        [else
                        (equal?/recur a b stx=?)]))





                        share|improve this answer
























                          1












                          1








                          1






                          Here's a concrete example of one possible way you could do the "better approach" Leif Andersen mentioned.



                          I have used this in multiple places for testing purposes, though if anyone wanted to use it in non-test code, they would probably want to re-visit some of the design decisions.



                          However, things like the equal?/recur pattern used here should be helpful no matter how you decide to define what equality means.



                          Some of the decisions you might want to make different choices on:




                          • On identifiers, do you want to check that the scopes are exactly the same (bound-identifier=?), or would you want to assume that they would be bound outside of the syntax object and check that they are bound to the same thing, even if they have different scopes (free-identifier=?)? Note that if you choose the first one, then checking the results of macro expansion will sometimes return #false because of scope differences, but if you choose the second one, then if any identifier is not bound outside of the syntax object, then it would be as if you only care about symbol=? equality on names, so it will return #true in some places where it shouldn't. I chose the first one bound-identifier=? here because for testing, a "false positive" where the test fails is better than a "false negative" where the tests succeeds in cases it shouldn't.


                          • On source locations, do you want to check that they are equal, or do you want to ignore them? This code ignores them because it's only for testing purposes, but if you want equality only for things which have the same source location, you might want to check that using functions like build-source-location-list.


                          • On syntax properties, do you want to check that they are equal, or do you want to ignore them? This code ignores them because it's only for testing purposes, but if you want to check that you might would to check that using functions like syntax-property-symbol-keys.



                          Finally here is the code. It may not be exactly what you want depending on how you answered the questions above. However, its structure and how it uses equal?/recur might be helpful to you.



                          (require rackunit)

                          ;; Works on fully wrapped, non-wrapped, and partially
                          ;; wrapped values, and it checks that the the inputs
                          ;; are wrapped in all the same places. It checks scopes,
                          ;; but it does not check source location.
                          (define-binary-check (check-stx=? stx=? actual expected))

                          ;; Stx Stx -> Bool
                          (define (stx=? a b)
                          (cond
                          [(and (identifier? a) (identifier? b))
                          (bound-identifier=? a b)]
                          [(and (syntax? a) (syntax? b))
                          (and (bound-identifier=? (datum->syntax a '||) (datum->syntax b '||))
                          (stx=? (syntax-e a) (syntax-e b)))]
                          [else
                          (equal?/recur a b stx=?)]))





                          share|improve this answer












                          Here's a concrete example of one possible way you could do the "better approach" Leif Andersen mentioned.



                          I have used this in multiple places for testing purposes, though if anyone wanted to use it in non-test code, they would probably want to re-visit some of the design decisions.



                          However, things like the equal?/recur pattern used here should be helpful no matter how you decide to define what equality means.



                          Some of the decisions you might want to make different choices on:




                          • On identifiers, do you want to check that the scopes are exactly the same (bound-identifier=?), or would you want to assume that they would be bound outside of the syntax object and check that they are bound to the same thing, even if they have different scopes (free-identifier=?)? Note that if you choose the first one, then checking the results of macro expansion will sometimes return #false because of scope differences, but if you choose the second one, then if any identifier is not bound outside of the syntax object, then it would be as if you only care about symbol=? equality on names, so it will return #true in some places where it shouldn't. I chose the first one bound-identifier=? here because for testing, a "false positive" where the test fails is better than a "false negative" where the tests succeeds in cases it shouldn't.


                          • On source locations, do you want to check that they are equal, or do you want to ignore them? This code ignores them because it's only for testing purposes, but if you want equality only for things which have the same source location, you might want to check that using functions like build-source-location-list.


                          • On syntax properties, do you want to check that they are equal, or do you want to ignore them? This code ignores them because it's only for testing purposes, but if you want to check that you might would to check that using functions like syntax-property-symbol-keys.



                          Finally here is the code. It may not be exactly what you want depending on how you answered the questions above. However, its structure and how it uses equal?/recur might be helpful to you.



                          (require rackunit)

                          ;; Works on fully wrapped, non-wrapped, and partially
                          ;; wrapped values, and it checks that the the inputs
                          ;; are wrapped in all the same places. It checks scopes,
                          ;; but it does not check source location.
                          (define-binary-check (check-stx=? stx=? actual expected))

                          ;; Stx Stx -> Bool
                          (define (stx=? a b)
                          (cond
                          [(and (identifier? a) (identifier? b))
                          (bound-identifier=? a b)]
                          [(and (syntax? a) (syntax? b))
                          (and (bound-identifier=? (datum->syntax a '||) (datum->syntax b '||))
                          (stx=? (syntax-e a) (syntax-e b)))]
                          [else
                          (equal?/recur a b stx=?)]))






                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Nov 23 at 16:31









                          Alex Knauth

                          4,3921822




                          4,3921822






























                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Stack Overflow!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              To learn more, see our tips on writing great answers.





                              Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                              Please pay close attention to the following guidance:


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53437701%2fhow-can-i-compare-syntax-objects-in-racket%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

                              Calculate evaluation metrics using cross_val_predict sklearn

                              Insert data from modal to MySQL (multiple modal on website)