I'm writing a #lang using a custom #%app that transforms all multi-argument 
applications into nested unary applications. I've run into trouble where a 
macro (written in standard Racket but to be used in the #lang) is 
malfunctioning because when the expander processes the macro's output, it uses 
the standard #%app instead of the custom one.

Here's a minimal implementation. The first bits are pretty self-explanatory. 
The last part is a simplified version of the macro in question, intended to 
define Church encodings of variant data.

#lang racket

(provide #%module-begin
         (rename-out [-define define]
                     [-λ λ]
                     [-#%app #%app]
                     [-encoding encoding]))

(require syntax/parse/define
         (for-syntax racket/sequence))

(define-syntax (-define stx)
  (syntax-case stx ()
    [(-define (name arg* ...) b0 b* ...)
     #'(define name (-λ (arg* ...) b0 b* ...))]
    [(-define name e)
     #'(define name e)]))

(define-syntax (-λ stx)
  (syntax-case stx ()
    [(-λ () b0 b* ...)
     #'(let () b0 b* ...)]
    [(-λ (arg) b0 b* ...)
     #'(λ (arg) b0 b* ...)]
    [(-λ (arg0 arg* ...) b0 b* ...)
     #'(λ (arg0) (-λ (arg* ...) b0 b* ...))]))

(define-syntax (-#%app stx)
  (syntax-case stx ()
    [(-#%app f)
     #'f]
    [(-#%app f arg)
     #'(f arg)]
    [(-#%app f arg0 arg* ...)
     #'(-#%app (f arg0) arg* ...)]))

(define-syntax-parser -encoding
  [(-encoding (name:id fields:id ...) ...)
   
   (define callback-id* (generate-temporaries #'(name ...)))
   
   (define cons-defn*
     (for/list ([name+fields (in-syntax #'((name fields ...) ...))]
                [this-callback callback-id*])
               #`(-define #,name+fields
                       (-λ #,callback-id*
                           (#,this-callback #,@name+fields)))))
   
   #`(begin #,@cons-defn*)])

This code will demonstrate the problem (with the above code in a module 
"lang-test.rkt"):

(module demo "lang-test.rkt"
  (define (s x y z) (x z (y z)))
  (encoding (nil)
            (cons car cdr)))

With expansion finished, the macro stepper shows the following.

(module demo "lang-test.rkt"
  (#%module-begin
   (module configure-runtime '#%kernel
     (#%module-begin
      (#%require racket/runtime-config)
      (#%app configure '#f)))
   (define-values (s)
     (lambda (x)
       (lambda (y) (lambda (z) (#%app (#%app x z) (#%app y z))))))
   (define-values (nil)
     (let-values:18 ()
       (lambda:19 (nil1:21)
         (lambda:22 (cons2:24) (#%app:25 nil1:21 nil)))))
   (define-values:27 (cons)
     (lambda:28 (car)
       (lambda:30 (cdr)
         (lambda:32 (nil1:21)
           (lambda:34 (cons2:24) (#%app:36 cons2:24 cons car cdr))))))))

You can see that the define form for 's' expands into a properly curried 
application, i.e., (x z (y z)) becomes ((x z) (y z)). But the applications in 
the constructors do not: we get (cons2 cons car cdr) instead of (((cons2 cons) 
car) cdr).

I can't figure out what's going on. Stepping through the transformation, I can 
clearly see the right #%app being added in the one case and doing its thing, 
and the wrong #%app being added in the other, but no indication of why. 
Examining lexical context information hasn't revealed any distinctions between 
the two cases. I thought maybe the use of generated temporaries might be the 
difference, but that was a dead end. If anyone can point out what I'm missing 
and how to fix it, I would be grateful. Thanks!

(Note: I did come up with a band-aid of explicitly inserting a -#%app at the 
beginning of the application in the output syntax, which is sufficient in this 
case but probably not sustainable.)

-- 
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.

Reply via email to