4

I've been stuck with an issue for a number of hours now. I'm trying to define a DSL using Racket's language extension features. I want to do something like the following pseudo-code. Ultimately I'd like to generate functions and macros given the input in the DSL, and most of that seems to work now, the problem is providing definitions which should work at the same level as the declarations. Is this even possible? It's late, and I'm sure I'm missing something really trivial. The most basic example of the problem is this:

tinylang.rkt:

#lang racket

; here we redefine module begin.
(provide (all-defined-out)
         (except-out (all-from-out racket) #%module-begin)
         (rename-out [module-begin #%module-begin])
         )

(define-syntax (module-begin stx)
  (syntax-case stx ()
    [(_ stmts ...)
     #`(#%module-begin
       (define (hello) (print "Yes!") (newline))
       ; (provide (for-syntax hello))
       (print "Function defined.")
       stmts ...   )]))

Now I try to use this new language elsewhere:

try.rkt:

#lang s-exp "tinylang.rkt"
(hello)

But I get the error "hello: unbound identifier in module in: hello", when loading the second module.

RogerTheDragon
  • 267
  • 1
  • 10

1 Answers1

5

The problem is that hello is defined in the lexical scope of tinylang.rkt but you want it to be in scope in try.rkt. You can use datum->syntax to set the lexical context of a piece of syntax.

This will fix the problem:

#lang racket

; here we redefine module begin.
(provide (all-defined-out)
         (except-out (all-from-out racket) #%module-begin)
         (rename-out [module-begin #%module-begin])
         )

(define-syntax (module-begin stx)
  (syntax-case stx ()
    [(_ stmts ...)
     #`(#%module-begin
       #,(datum->syntax 
          stx
          (syntax->datum 
           #'(define (hello) (print "Yes!") (newline))))
       (print "Function defined.")
       stmts ...   )]))

UPDATE:

In response to comments, the previous solution could be simplified to:

(define-syntax (module-begin stx)
  (syntax-case stx ()
    [(_ stmts ...)
     (with-syntax ([hello-fn (datum->syntax stx 'hello)])
       #`(#%module-begin
          (define (hello-fn) (print "Yes!") (newline))
          (print "Function defined.")
          stmts ...   ))]))
stchang
  • 2,555
  • 15
  • 17
  • 1
    Could the `(syntax->datum #'____))` part simply be `'____`? Not a nitpicky/rhetorical question --- although I think the answer is yes, I'm genuinely asking. – Greg Hendershott Sep 16 '13 at 18:58
  • 3
    Isn't the `syntax->datum` covering a little too much? Surely you want it to only cover `hello`, while keeping `define`, `print`, and `newline` hygienic. – C. K. Young Sep 16 '13 at 21:15
  • 1
    Yes, you are both correct of course. I'll update my response. Thanks. – stchang Sep 16 '13 at 21:18
  • Much better. But please fix your indentation. :-D – C. K. Young Sep 16 '13 at 21:21
  • @stchang, thanks for a great, detailed answer! I would like a little clarification though, because I think I'm misunderstanding something which is making me end up with this sort of problem. The misunderstanding is: how can it be that `hello` (in the broken code) ends up in scope in tinylang.rkt? Apparently it's not as simple to think about as assuming that the module header (well, the application of `#%module-begin`) will be replaced by the plain syntax I've written in tinylang, and then evaluated later, it seems there's some evaluation happening anyway? Can't I just "splice" some code? – RogerTheDragon Sep 17 '13 at 08:47
  • 1
    I meant that your original `hello` has the context of the syntax where it's defined, which is different from its use. Your intuition is correct. It's not as simple as just cobbling together pieces of syntax and evaluating it. The reason has to do with favoring local reasoning. Imagine another definition of `hello` in your original `try.rkt`. Which should the call to `(hello)` refer to? The Racket architects (among others) decided that favoring the local definition makes programs easier to reason about. The behavior you want is the uncommon case and thus takes more work to pull off. – stchang Sep 17 '13 at 17:44
  • You might benefit from watching Matthew Flatt's recent [Clojure/West talk](http://www.infoq.com/presentations/racket): Metaprogramming time! It's hour long but I think it will help your understanding of things. – stchang Sep 17 '13 at 17:45