Re: [racket-users] Problem with macro which implements selectors for mutable pairs
Hi Joan, As Alex writes using define-macro is not recommended. It can be used to write very simple macros, but if you try anything complicated then you are likely to run into problems. The best strategy is therefore to use define-syntax from the beginning. The solution below has a lot in common with the solution from Alex, but I have attempted to use syntax-parse only to pick out elements from the input syntax. There are two versions of the macro with-mcxrs/explicit and with-mcxr. The first require the user to explicitly write which identifiers he needs. My initial thought was to let with-mcxrs search for identifiers and then expand into with-mcxrs/explicit. I decided to take another approach and let the expander find the identifiers for me. In racket applications like (mcar xs) expand into (#%app mcar xs). If we rebind #%app we can make an application that treats applications of the type (mxcr expr) differently from other applications. If you need mcar to work as variable reference too, then that could be added using a so-called set!-transformer. Anyways, here is the code: #lang racket (require compatibility/mlist (for-syntax syntax/parse)) ;;; Utilites used by the macros below. (begin-for-syntax ; mcxr-string? : string -> boolean(ish) ; is the string s of the form mcxr where ; x is a sequence of a's and d's? (define (mcxr-string? s) (regexp-match "^mc(a|d)*r$" s)) ; mcxr-identifier? : syntax -> boolean ; is stx an identifier whose name is of the form mcxr ? (define (mcxr-identifier? stx) (and (identifier? stx) (mcxr-string? (symbol->string (syntax-e stx) ; mcxr-identifier->xs : identifier -> list-of-symbols ; Given an identifier of the type mcxr: ; returns a list of a's and d's matching the x ; > (mcxr-identifier->xs #'mcaadar) ; '(a d a a) ; Note the order is reversed. (define (mcxr-identifier->xs id) (define str (symbol->string (syntax-e id))) ; remove mc and r from the start and end respectively (define x-str (substring str 2 (- (string-length str) 1))) (map (λ (c) (string->symbol (string c))) (reverse (string->list x-str) ; SYNTAX (mcxr (x ...) expr) where x is either a or d ; Example: ; (mxcr (d d a) e) expands to (mcar (mcdr (mcdr e))) ; (mxcr mcaddr e) expands to (mxcr (d d a) e) which expands to (mcar (mcdr (mcdr e))) (define-syntax (mcxr stx) (define error-str (string-append "expected (mcxr (x ...) expr) or (mcxr id expr) " "where x is a or d " "and id is of the form mcxr")) (syntax-parse stx #:datum-literals (a d) [(_mcxr () expr) #'expr] [(_mcxr (a x ...) expr) #'(mcxr (x ...) (mcar expr))] [(_mcxr (d x ...) expr) #'(mcxr (x ...) (mcdr expr))] [(_mcxd idexpr) (if (mcxr-identifier? #'id) (with-syntax ([(x ...) (mcxr-identifier->xs #'id)]) #'(mcxr (x ...) expr)) (raise-syntax-error 'mcxr error-str stx))] [_ (raise-syntax-error 'mcxr error-str stx)])) ; SYNTAX (with-mcxrs/explicit-ids (id ...) body ...) ; id is an identifier of the form mcxr ; the given identifiers will be bound in body ; to a function matching their name. ; For example mcadr will be bound to (lambda (v) (mcar (mcdr v))) in body. (define-syntax (with-mcxrs/explicit-ids stx) (syntax-parse stx [(_ (id ...) body ...) (with-syntax ([(xs ...) (map mcxr-identifier->xs (syntax->list #'(id ...)))]) #'(let ([id (lambda (v) (mcxr xs v))] ...) body ...))])) (with-mcxrs/explicit-ids (mcaar mcdar mcaadr mcdadr) (let ((a (mcons (mcons 1 2) (mcons (mcons 3 4) '() (list (mcaar a) (mcdar a) (mcaadr a) (mcdadr a ;;; The downside of the above macro is that the user of with-mcxrs/explicit-ids ;;; needs to specify which functions he needs. ;;; If we assume that he only want's to apply mcxr, then we can ;;; make a new version of application (#%app) that rewrites ;;; applications of the form (mcxr expr) but leaves other applications ;;; untouched. (define-syntax (mcxr-app stx) (syntax-parse stx [(_mcxr-app id expr) (cond [(mcxr-identifier? #'id) #'(mcxr id expr)] [else #'(id expr)])] [(_#%app expr arg ...) #'(#%app expr arg ...)])) ;;; Given mcxr-app we can now write with-mcxrs like this, ;;; by locally rebinding #%app to mcxr-app in body. (define-syntax (with-mcxrs stx) (syntax-parse stx [(_ body ...) (with-syntax ([new-#%app (syntax-local-introduce (datum->syntax #'stx '#%app #'here))]) #'(let-syntax ([new-#%app (make-rename-transformer #'mcxr-app)]) body ...))])) (with-mcxrs (let ((a (mcons (mcons 1 2) (mcons (mcons 3 4) '() (list (mcaar a) (mcdar a)
Re: [racket-users] Problem with macro which implements selectors for mutable pairs
> On May 12, 2016, at 4:45 AM, Joan Martinwrote: > > Hi, > I'm trying to program a macro which defines different selectors for mutable > pairs. > I know in Racket it's possible to use only mcar and mcdr, but mcdar etc. > could be useful for me. > > I would like to use macro this way: > > (with-mcxrs > (let ((a (mcons (mcons 1 2) (mcons (mcons 3 4) '() > (list (mcaar a) > (mcdar a) >(mcaadr a) >(mcdadr a > > Mcxrs will be defined in macro's body and then whole expresion would be > evaluated - now with bindings in macro's environment so no error should > appear. > I think procedure seek and macro defines3 are ok (when using separately they > work fine) but in a macro "with-mcxrs" an error appears: mcaar undefined. There's a macro with a similar purpose in the implementation of the racket lens library, although instead of a `with-mcxrs` form, it was basically a `define-mcxrs` form, except it was for lenses instead of mutable pairs. The code is here if you're interested: https://github.com/jackfirth/lens/blob/master/lens/private/list/cadr-etc.rkt Because it behaves like a `define-mcxrs` form, it can only define a fixed number of `cadr`-like functions, but there are a fixed number of existing `cadr`-like functions anyway. > Where's the problem? I can't figure it out. I would be glad if anyone could > give me some advice. I noticed you're using `define-macro` for both `with-mcxrs` and `defines3`, and you're generating lists using `quasiquote`. But in racket there are much better ways of doing things. For one thing, if you use `quasiquote`, you don't get any control over bindings, and without that, the bindings you're introducing aren't visible to the macro user, because it's in the wrong lexical scope. To fix that, you can use a form like `define-syntax`, with a pattern matching tool like `syntax-case` or `syntax-parse`. For that I would strongly recommend Greg Hendershott's Fear of Macros tutorial: http://www.greghendershott.com/fear-of-macros/ But if you don't have time to read that right now, here's how I would define it that way. Instead of symbols and lists, `define-syntax` macros use syntax objects, which are just a thin wrapper around them containing source location and lexical scoping information. Instead of quote, there's syntax. Instead of quasiquote, unquote, and unquote-splicing, there's quasisyntax, unsyntax, and unsyntax-splicing. Instead of ', `, ,, and ,@, the abbreviations are #' for syntax, #` for quasisyntax, #, for unsyntax, and #,@ for unsyntax-splicing. There's also stx-car, stx-cdr, stx-map, etc. Also, an identifier is just a syntax value that wraps a symbol. First, I would define some helper functions for taking an mcxr identifier and getting an implementation for it: (begin-for-syntax ;; mcxr->mcxr-implementation : Identifier -> Syntax (define (mcxr->mcxr-implementation id) (define str (symbol->string (syntax->datum id))) (define x-list (string->list (substring str 2 (sub1 (string-length str) (x-list->mcxr-implementation x-list)) ;; x-list->mcxr-implementation : (Listof Char) -> Syntax (define (x-list->mcxr-implementation x-list) #`(compose #,@(map x-char->mcxr-id x-list))) ;; x-char->mcxr-id : Char -> Identifier (define (x-char->mcxr-id char) (cond [(char=? char #\a) #'mcar] [(char=? char #\d) #'mcdr] [else (error "expected the character 'a' or 'd', got ~v" char)])) ) The macro also has to find all of the mcxr-like identifiers in the body, so I would also define helper functions for that: (require (for-syntax syntax/parse)) (begin-for-syntax ;; mcxr-id? : Identifier -> Boolean ;; returns true if the identifier matches the form mc[ad]*r (define (mcxr-id? id) (mcxr-string? (symbol->string (syntax->datum id ;; mcxr-string? : String -> Boolean (define (mcxr-string? str) (regexp-match? #rx"^mc[a|d]*r$" str)) ;; find-mcxr-ids : Any (Listof Identifier) -> (Listof Identifier) ;; the ids argument is an accumulator for the ids found so far (define (find-mcxr-ids stx ids) (syntax-parse stx [mcxr:id #:when (mcxr-id? #'mcxr) (cons #'mcxr ids)] [(a . b) (find-mcxr-ids #'a (find-mcxr-ids #'b ids))] [else ids])) ) And finally, the macro: (require (for-syntax racket/list syntax/stx)) (define-syntax with-mcxrs (lambda (stx) (syntax-parse stx [(_ . body) #:with [mcxr-id ...] (remove-duplicates (find-mcxr-ids #'body (list)) bound-identifier=?) #:with [mcxr-impl ...] (stx-map mcxr->mcxr-implementation #'[mcxr-id ...]) #'(let ([mcxr-id mcxr-impl] ...) . body)]))) If you read Fear of Macros, this `#:with` stuff is similar to using the `with-syntax` form. Trying it out with your example: > (with-mcxrs (let ([a (mcons (mcons 1 2) (mcons (mcons 3 4) '()))]) (list (mcaar a) (mcdar a) (mcaadr a) (mcdadr
[racket-users] Problem with macro which implements selectors for mutable pairs
Hi, I'm trying to program a macro which defines different selectors for mutable pairs. I know in Racket it's possible to use only mcar and mcdr, but mcdar etc. could be useful for me. I would like to use macro this way: (with-mcxrs (let ((a (mcons (mcons 1 2) (mcons (mcons 3 4) '() (list (mcaar a) (mcdar a) (mcaadr a) (mcdadr a Mcxrs will be defined in macro's body and then whole expresion would be evaluated - now with bindings in macro's environment so no error should appear. I think procedure seek and macro defines3 are ok (when using separately they work fine) but in a macro "with-mcxrs" an error appears: mcaar undefined. Where's the problem? I can't figure it out. I would be glad if anyone could give me some advice. And could "defines3" be out of macro "with-mcxrs"? Or should it be inside - like in my code? Thanks a lot. Code: first I go though whole body and I store found mcxrs in list, which is called "selectors" in let loop. And then call another macro "defines3" which should return procedure - for example: (lambda (pair) (mcar (mcdr (mcar pair. And this procedure should be defined as a value of particular mcaar or mcdar etc. (define-macro with-mcxrs (lambda body `(define-macro defines3 (lambda (chars) (let ((loop (gensym))) `(lambda (pair) (let ,loop ((chsez ,chars)) (if (null? chsez) pair (if (equal? (car chsez) #\a) (mcar (,loop (cdr chsez))) (mcdr (,loop (cdr chsez )) (define seek2 (let ((pom '() )) (lambda (l) (cond ((null? l) pom ) ((list? (car l)) (begin(seek2 (car l)) (seek2 (cdr l))) ) (else (if (not (symbol? (car l))) (seek2 (cdr l)) (let* ((test (symbol->string(car l))) (len (string-length test))) (if (> len 4) (if (or (equal? (substring test 0 3) "mca") (equal? (substring test 0 3) "mcd")) (begin (set! pom (cons (car l) pom) ) (seek2 (cdr l))) (seek2 (cdr l))) (seek2 (cdr l)) (let ((loop (gensym))) `(let ,loop ((selectors (,seek2 (quote ,@body)) )) ;;list of selectors (cond ((null? selectors) (,@body)) (else (define (car selectors) (defines3 (string->list (substring (car l) 2 (- (string-length (car l))1)) ))) (,loop (cdr selectors))) ))) )) -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.