`__FILE__` and `__LINE__` in Racket
Many languages have a variable (or preprocessor macro) called __FILE__
or __file__
whose value is the pathname of the current source file. Likewise __LINE__
for the the source line number.
You probably need this less in Racket than you imagine. For example:
-
We wouldn’t test that
__FILE__
ends inmain.rkt
; instead we’d use amain
submodule(module+ main <your code here>)
. -
To get a data file
foo.dat
located in the same directory as a source file we’d use(define-runtime-path foo.dat "foo.dat")
. If we’re a package or executable this works regardless of where our files happen to get installed.
But if you really did need a __FILE__
in Racket, how would you do it?
Simply:
1 2 |
(syntax-source #'here) ;full path to the source file, i.e. __FILE__ (syntax-line #'here) ;line number, i.e. __LINE__ |
You could use #'0
or #'42
instead. The only important thing about #'here
is that it’s a syntax object representing syntax from the current source file. The #'
prefix is what matters; it’s reader shorthand for the syntax
function. So #'here
is read as (syntax
here)
, which you could also use if you don’t mind typing a few more keys.
1 2 3 |
;; Same as above (syntax-source (syntax here)) ;full path to the source file, i.e. __FILE__ (syntax-line (syntax here)) ;line number, i.e. __LINE__ |
In some Lisps, a macro works on a plain s-expression — the 0
or 42
or here
or (+ 1 1)
. A syntax object in Racket contains the s-expression, and also information such as source location (line, column, position, span) and lexical scope.1
So in Racket, you simply need to make some syntax object in your source file, like #'here
, and feed it to syntax object accessors like syntax-source
and syntax-line
.2
What if you want to write __FILE__
and __LINE__
like in other languages? You could define these as macros in a file, and require
and use where desired:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#lang racket/base (provide __FILE__ __LINE__) (require (for-syntax racket/base)) (define-syntax (__FILE__ stx) ;; `stx` comes from where the macro was invoked, so give _that_ to ;; `syntax-source`. (with-syntax ([file (syntax-source stx)]) (syntax-case stx () [_ #'file]))) (define-syntax (__LINE__ stx) ;; `stx` comes from where the macro was invoked, so give _that_ to ;; `syntax-line`. (with-syntax ([line (syntax-line stx)]) (syntax-case stx () [_ #'line]))) |
In this case, we care about the source file and line from where the macro was invoked — from where we typed __FILE__
or __LINE__
. So we’re careful to give that input stx
to source-file
or source-line
. (Whereas if we used some made-up syntax object literal like #'here
, it would give us the source or line of #'here
in the macro definition.)
It turns out that learning how to do a __FILE__
in Racket ends up being a gentle, practical introduction to syntax objects.
-
For more about syntax objects see my Fear of Macros. Or if you’re coming to Racket from another Lisp, it might be quicker to read Eli Barzilay’s blog post. ↩
-
You might be wondering, why isn’t there an error that no identifier named
here
is defined? Great question. A syntax object such as#'here
a.k.a.(syntax here)
is basically quoted data — like'here
a.k.a.(quote here)
. We’re noteval
uating the syntax. ↩