I followed this QA to set the current directory. It seems work:
#lang racket
(current-directory)
; #<path:/home/user/foo>
(current-directory
(expand-user-path "~/foo/bar_lib")
)
(current-directory)
; #<path:/home/user/foo/bar_lib>
But then I failed to load one file (This says more about why load
should not be used load file normally):
#lang racket
; do the same as the above
(require "foo.rkt")
;; It will still use the old dir
; path: /home/user/foo/foo.rkt
Q1: How to change the current directory and load one file from there in Racket, e.g. load /home/user/foo/bar_lib/foo.rkt
in the above?
I didn't use directly (require "bar_lib/foo.rkt")
directly here because I can't ensure its relative dir is unchanged. That means I am a bit confusing about the doc saying for rel-string
:
A path relative to the containing source (as determined by
current-load-relative-directory
orcurrent-directory
).
I don't know how that is done with those 2 commands. (current-directory)
always give the directory where I run racket file.rkt
or the value set before by (current-directory target-dir-str)
. current-load-relative-directory
is similar except that its default value is #f
.
I tried to check the source codes by keyword "require" but it seems to be implemented by C. Trying to understand that is a bit of overkill to only want to load one lib.
Q2: Does the doc mean rel-string
must be based on the path of the file run, e.g. ~/foo/
for ~/foo/main.rkt
? If so, then one workaround is found.
It is safe to use relative paths like (require "bar_lib/foo.rkt")
for the same reason current-directory
doesn't have the effect you expect.
Expressions within a module definition such as your main.rkt
or foo.rkt
are not fully evaluated in order like they are when entered one-by-one at a REPL.
The evaluation of a module form does not evaluate the expressions [as opposed to definitions] in the body of the module.... A module body is executed only when the module is explicitly instantiated via require...
Because it's a syntactic form, require
operates at a higher phase level than ordinary expressions like (current-directory "some-dir")
. We can use begin-for-syntax
to elevate to require
's phase level to see what it would see:
cd.rkt
#lang racket
(current-directory "/tmp")
(begin-for-syntax
(printf "dir = ~a~n" (current-directory)))
$ racket cd.rkt
dir = /home/user/
$ racket
> (require "cd.rkt")
dir = /home/user/
; now in /tmp
dir = /tmp/
> (current-directory)
#<path:/tmp/>
This confirms the documentation's statement that module body expressions are executed only when the module is explicitly instantiated. (dir =
is printed twice for reasons I won't go into here.)
So while it would be possible to set the current directory in the same phase as require
, it's rarely done in practice, as evidenced by the code in the racket repos. Additionally, it may not have the effect you intend. The current-load-relative-directory
parameter supersedes current-directory
, and current-load/use-compiled
(called by require
) is expected to manage current-load-relative-directory
, potentially ignoring whatever you might have set it to.