[racket] define-provide-syntax + contract-out

From: Matthew Butterick (mb at mbtype.com)
Date: Thu Feb 27 11:26:50 EST 2014

I've learned about 'define-provide-syntax', and it works as expected in macro1 below with rename-out.

Macro2, which uses contract-out, doesn't work, reporting the error "contract-out: not a provide sub-form." I gather this is the result of the macro not seeing a binding for contract-out. 

But where is it supposed to get the binding? I've moved (require racket/contract) around the macro to no avail.

PS. What is the point of this macro? To define a function with a contract in the usual way in the module source, but then move the contract inside a submodule, so it is only invoked on request.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#lang racket/base

(module macro1 racket
  (require racket/provide-syntax)
  
  (provide define-special)
  
  (define-provide-syntax (r-out stx)
    (syntax-case stx ()
      [(_ name)
       #'(rename-out [name foobar])]))
  
  (define-syntax (define-special stx)
    (syntax-case stx ()
      [(_ (name arg) body ...)
       #'(begin
           (module+ inner
             (define (name arg) body ...)
             (provide (r-out name))))])))


(module macro2 racket
  (require racket/provide-syntax)
  (require racket/contract) ; nope
  (require (for-syntax racket/contract)) ; nope
  
  (provide define-special2)
  
  (define-provide-syntax (c-out stx)
    (syntax-case stx ()
      [(_ name contract)
       #'(contract-out [name contract])]))
  
  (define-syntax (define-special2 stx)
    (syntax-case stx ()
      [(_ (name arg) contract body ...)
       #'(begin
           (define (name arg) body ...)
           (module+ safe
             (require racket/contract) ; nope
             (provide (c-out name contract))))])))

(require 'macro1 'macro2)

(define-special (func x) x)
;> [works fine]
(define-special2 (func2 x) (string? . -> . string?) x)
;> contract-out: not a provide sub-form in: (contract-out (func2 (-> string? string?)))

Posted on the users mailing list.