At Tue, 1 Jan 2013 17:59:21 -0500, Sean Kanaley wrote: > While I've ultimately succeeded in having it return the correct output for > a sample input, I'm not positive it's correct in general and I *am *positive > it's written poorly, as I don't fully understand both syntax-case and > syntax objects vs. datums. If someone could look it over and provide a > more canonical version, I would be grateful. > > Here's how the macro works: > > > (condlet (((= 1 2) (x (princ ’a)) (y (princ ’b))) > ((= 1 1) (y (princ ’c)) (x (princ ’d))) > (t (x (princ ’e)) (z (princ ’f)))) > (list x y z)) > *CD > (D C NIL)* > > Before I post the horrible racket code, I will explain the problems I'm > having with macros in general: > > Problem 1, separate phases: I have a remove-duplicates-by function that > would be great to have globally, but it seemingly must be written locally.
You can use `define-for-syntax' to define functions to be used at expansion time. More generally, `begin-for-syntax' runs code at expansion time. You can also `require' libraries for syntax time by using the `for-syntax' `require' sub-form. In your example, you could define `remove-duplicates-by' like this: (define-for-syntax (remove-duplicates-by f l) (let R ((l l)) (if (null? l) null (cons (car l) (R (remove* (list (car l)) (cdr l) (λ (a b) (eq? (f a) (f b))))))))) and you would be able to use it in your macro. > Problem 2: You can't use a pattern variable outside of a pattern, so you > have to syntax-ify it with #', but then you can't access the associated > s-exp without removing the syntax. This blog post explains the relationship between syntax objects and s-expressions: http://blog.racket-lang.org/2011/04/writing-syntax-case-macros.html > The way to bind things to null by > default is to get every id and output the obvious let statement, except ids > might be repeated so you have to remove duplicates (enter problem 1). It's > remove-duplicates-BY because the removal happens by syntax->datum'ing each > identifier-syntax-thing since it can't appear outside of a pattern. Comparing identifiers using `syntax->datum' can equate identifiers that are different, but happen to have the same name. Identifiers have their own comparison functions that do the right thing. `free-identifier=?' is what you want here. Also, `racket/list''s `remove-duplicates' takes an optional comparison argument, so you can pass it `free-identifier=?'. It also takes a `#:key' argument, which makes it act like your `remove-duplicates-by'. To do this, you would need to `(require (for-syntax racket/list))'. > Problem 2: How to remove a portion of the macro code into a separate > transformer function? It's kind of annoying having a whole block of code > relegated to cleaning up the duplicate ids inside of the let it expands > into. That would ideally be written "let #,(remove-dups #'(c cs...))" or > similar...some kind of sub-macro to handle just getting ids. I thought > that's what let-syntax or nested define syntaxes were for but I get phase > errors or preposterous, very dark errors like "lambda not bound". The blog post I linked above has an explanation of phase distinctions. In general, if you want to use a function (or a macro) in a macro (as opposed to expanding into it), that function (or macro) needs to be imported for syntax. For convenience, `#lang racket' provides all of `racket/base' for syntax. Vincent ____________________ Racket Users list: http://lists.racket-lang.org/users