There's a simple code section I'd like to share among a few racket scripts I wrote. I don't want to (require ..) it, but want to run it at top level (the common code includes calls to (current-command-line-arguments), etc. that a required module doesn't understand (at least I don't know if that's possible).
I try (load)'ing it, but it doesn't work as it was part of the current top-level context - I get an unbound identifier error in the loading context for a definition that's defined in the common header.
It seems I still don't understand some sections of the chapter 14 of the racket reference - I tried some examples of 14.1 (namespaces), 14.3 (racket/load) but couldn't get it working.
What am I missing? How can we make such a seemingly simple case work?
Edit: Here is an example from my case:
this is header.rkt:
#lang racket/load
(define-for-syntax args (current-command-line-arguments))
(define-for-syntax PROTOTYPING (or
(= (vector-length args) 0)
(equal? (vector-ref args 0) "local")))
(define-syntax (if-prototyping stx)
(syntax-case stx ()
((_ debug-expr non-debug-expr)
(if PROTOTYPING
#'debug-expr
#'non-debug-expr))))
(if-prototyping
(require (prefix-in "ci:" (file "/debugenvpath/utils.rkt")))
(require (prefix-in "ci:" (file "/releaseenvpath/utils.rkt"))))
; arrange arguments
(define args (current-command-line-arguments))
(if-prototyping
(set! args (if (= (vector-length args) 0)
(vector "arg1")
(vector (vector-ref args 1))))
#t)
and this is a file that uses it (let's call this test.bat):
; @echo off
; "C:\Program Files\Racket\racket.exe" "%~f0" %*
; exit /b
#lang racket
(require "header.rkt")
"hello, args are: "
args
and running test.bat gives error below for the last line of the file:
args: unbound identifier in module in: args
Update2
OK, here is an update with phase1 (so-called macro-time) & phase0 (so-called runtime) expansion of my approach:
I can run (current-command-line-arguments) in phase1 (macro) when everything is in one file:
#lang racket
(define-for-syntax args (current-command-line-arguments))
(define-for-syntax PROTOTYPING (or
(begin
(printf "phase1 expansion, args are: ~a\n" args)
(= (vector-length args) 0))
(equal? (vector-ref args 0) "local")))
(begin-for-syntax
(printf "phase1, val for PROTOTYPING is: ~a\n" PROTOTYPING))
(define-syntax (if-prototyping stx)
(syntax-case stx ()
((_ debug-expr non-debug-expr)
(if PROTOTYPING
#'debug-expr
#'non-debug-expr))))
(if-prototyping
(require (prefix-in "ci:" (file "./debug-env/util.rkt")))
(require (prefix-in "ci:" (file "./release-env/util.rkt"))))
; arrange arguments
(define args (current-command-line-arguments))
(printf "phase0 expansion, args are: ~a\n" args)
(if-prototyping
(set! args (if (= (vector-length args) 0)
(vector "my-testing-arg-to-use-from-drracket")
(vector (vector-ref args 1))))
#t)
"util func call: "
(ci:f)
have util.rkt files for double checking: debug-env/util.rkt:
#lang racket
(provide f)
(define (f) (display "f from debug"))
release-env/util.rkt:
#lang racket
(provide f)
(define (f) (display "f from release"))
and this is the output of the test run:
>test-commonheader.bat local arg1 arg2
phase1 expansion, args are: #(local arg1 arg2)
phase1, val for PROTOTYPING is: #t
phase0 expansion, args are: #(local arg1 arg2)
"util func call: "
f from debug
>test-commonheader.bat arg1 arg2
phase1 expansion, args are: #(arg1 arg2)
phase1, val for PROTOTYPING is: #f
phase0 expansion, args are: #(arg1 arg2)
#t
"util func call: "
f from release
it works as I expected. Now, the problem standing is, I can't put this whole logic in a common-header.rkt file to reuse. I'm just doing some more reading on Racket Reference 3.2 (Importing and Exporting), and there's a lot more that I should understand on require'ing for-syntax or directly to a phase level and provide'ing to phase-levels. I progressed a bit more with (for-syntax) in require and provide, but not there yet :/
update 3
It seems I was too busy trying to understand the phase-level extensions I missed the basics again. A simple (provide if-prototyping) from the header and (require "header.rkt") is actually working - but not perfectly :( . Alex was somehow correct in suggesting require should work. I was trying to (provide (for-syntax if-prototyping)) but that doesn't work because the if-prototyping is used at phase0 in the importing module.
Here's the latest header:
#lang racket
(provide if-prototyping)
(define-for-syntax args (current-command-line-arguments))
(define-for-syntax PROTOTYPING (or
(begin
(printf "phase1 expansion, args are: ~a\n" args)
(= (vector-length args) 0))
(equal? (vector-ref args 0) "local")))
(begin-for-syntax
(printf "phase1, val for PROTOTYPING is: ~a\n" PROTOTYPING))
(define-syntax (if-prototyping stx)
(syntax-case stx ()
((_ debug-expr non-debug-expr)
(if PROTOTYPING
#'debug-expr
#'non-debug-expr))))
(if-prototyping
(begin
(require "debug-env/util.rkt")
(provide (all-from-out "debug-env/util.rkt")))
(begin
(require "release-env/util.rkt")
(provide (all-from-out "release-env/util.rkt"))))
and in the test-commonheader.bat I require the header:
(require "header.rkt")
now, a new problem is, header.rkt phase1 definitions run twice (which may be alright for this case - that's why I'll accept Alex's answer on using require):
>test-commonheader.bat arg1 arg2
phase1 expansion, args are: #(arg1 arg2)
phase1, val for PROTOTYPING is: #f
phase1 expansion, args are: #(arg1 arg2)
phase1, val for PROTOTYPING is: #f
phase0 expansion, args are: #(arg1 arg2)
#t
"util func call: "
f from release
(current-command-line-arguments)at afor-syntaxlevel won't work. The decisions you make based on the command-line arguments need to be somehow delayed until run-time, when the command-line arguments are available. - Alex Knauthloadbut that requires basically aneval-based approach to almost all your files. However, there are still much better ways, especially since I'm guessing "file1.rkt" and "file2.rkt" provide the same bindings with the same purpose. The same interface. I'll update my answer this afternoon. - Alex Knauth