Where does DrRacket look for files?

Sometimes DrRacket and the command line interface to Racket disagree even though one is, more or less, an IDE for the other. This disagreement can show up when one tries to load files using process or system, among others. You may very well find that DrRacket does one thing, whereas the same code, executed using the command line, does another. To put it more bluntly: one works, whereas the other does not. What’s going on?

It may very well be the environment.

The PATH Not Taken

(NB: The following discussion is based on my experience with Unix systems, including Linux and macOS. What I say probably has to be adapted—and may not even be true—in the case of running Racket on Windows.)

When executing Racket code using the command line, the initial Racket process will inherit whatever (exported) variables were around. When you’re working in DrRacket and you launched it without the help of the command line (e.g., by simply clicking on an icon in the macOS Dock or opening the application in the macOS Finder), you’re almost certainly working in a different environment.

How are they different?

Try evaluating this code in DrRacket, and in a plain old Racket REPL:

Do you see differences? In particular, do you see a difference in the PATH variable? I bet you do.

Here’s what I see when I run this code in DrRacket (Warning: ugly unwrapped long strings ahead):

'((#"USER" . #"jesse")
  (#"XPC_FLAGS" . #"0x0")
  (#"Apple_PubSub_Socket_Render" . #"/private/tmp/com.apple.launchd.Arf61gEFhw/Render")
  (#"LOGNAME" . #"jesse")
  (#"TMPDIR" . #"/var/folders/rx/lzgw7dzs0fqc1c7q1bv790_h0000gn/T/")
  (#"SSH_AUTH_SOCK" . #"/private/tmp/com.apple.launchd.gkjlEqASWZ/Listeners")
  (#"PATH" . #"/usr/bin:/bin:/usr/sbin:/sbin")
  (#"__CF_USER_TEXT_ENCODING" . #"0x1F5:0x0:0x0")
  (#"HOME" . #"/Users/jesse")
  (#"SHELL" . #"/bin/zsh")
  (#"XPC_SERVICE_NAME" . #"org.racket-lang.DrRacket.6672"))

And here’s what I see when I execute that code after invoking Racket from the command line, after all the elaborate setup in my .profile and .zshrc files have had a go at modifying my environment:

'((#"PAGER" . #"less")
  (#"ITERM_SESSION_ID" . #"w0t0p0:0BAC630D-7888-4A24-9958-87F4B2A2C41F")
  (#"MANPATH"
   .
   #"/usr/local/texlive/2016/texmf-dist/doc/man:/sw/share/man:/usr/local/share/man:/usr/share/man:/Library/Developer/CommandLineTools/usr/share/man:/sw/lib/perl5/5.18.2/man")
  (#"USER" . #"jesse")
  (#"TERM_SESSION_ID" . #"w0t0p0:0BAC630D-7888-4A24-9958-87F4B2A2C41F")
  (#"TERM" . #"xterm-256color")
  (#"XPC_FLAGS" . #"0x0")
  (#"OLDPWD" . #"/Users/jesse")
  (#"Apple_PubSub_Socket_Render"
   .
   #"/private/tmp/com.apple.launchd.Arf61gEFhw/Render")
  (#"LESS" . #"-R")
  (#"XDG_DATA_DIRS" . #"/sw/share")
  (#"LOGNAME" . #"jesse")
  (#"TMPDIR" . #"/var/folders/rx/lzgw7dzs0fqc1c7q1bv790_h0000gn/T/")
  (#"XDG_CONFIG_DIRS" . #"/sw/etc/xdg")
  (#"TERM_PROGRAM_VERSION" . #"3.0.13")
  (#"SSH_AUTH_SOCK" . #"/private/tmp/com.apple.launchd.gkjlEqASWZ/Listeners")
  (#"LC_CTYPE" . #"en_US.UTF-8")
  (#"PWD" . #"/Users/jesse")
  (#"PATH"
   .
   #"/Users/jesse/bin:/Users/jesse/context/tex/texmf-osx-64/bin:/usr/local/texlive/2016/bin/x86_64-darwin:/Users/jesse/.cargo/bin:/Users/jesse/anaconda/bin:/Applications/Racket v6.8/bin:/sw/bin:/sw/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin")
  (#"XDG_CACHE_HOME" . #"/Users/jesse/.cache")
  (#"COLORFGBG" . #"7;0")
  (#"TERM_PROGRAM" . #"iTerm.app")
  (#"INFOPATH"
   .
   #"/usr/local/texlive/2016/texmf-dist/doc/info:/sw/share/info:/sw/info:/usr/share/info")
  (#"CLASSPATH" . #"/sw/share/java/saxon/saxon9he.jar:.")
  (#"__CF_USER_TEXT_ENCODING" . #"0x1F5:0x0:0x0")
  (#"HOME" . #"/Users/jesse")
  (#"SHELL" . #"/bin/zsh")
  (#"XDG_RUNTIME_DIR" . #"/var/folders/rx/lzgw7dzs0fqc1c7q1bv790_h0000gn/C/")
  (#"SHLVL" . #"1")
  (#"XDG_DATA_HOME" . #"/Users/jesse/.local/share")
  (#"LANG" . #"en_US.UTF-8")
  (#"XPC_SERVICE_NAME" . #"0")
  (#"LSCOLORS" . #"Gxfxcxdxbxegedabagacad")
  (#"ZSH" . #"/Users/jesse/.oh-my-zsh")
  (#"_" . #"/Applications/Racket v6.8/bin/racket")
  (#"PERL5LIB" . #"/sw/lib/perl5:/sw/lib/perl5/darwin")
  (#"ITERM_PROFILE" . #"Default")
  (#"XDG_CONFIG_HOME" . #"/Users/jesse/.config"))

They’re definitely not the same.

Many of the differences between the two environments are immaterial. But if you’re running external programs (e.g., the psql command line client for the Postgres relational database), the difference means that

(system "psql")

may very well fail in one case but work in another.

How can you fix the problem?

Solution 1: Use absolute paths

One solution is to use absolute paths for programs. Instead of saying

(system "psql")

you’d write

(system "/usr/local/bin/psql")

By using absolute paths, any differences in the PATH environment variable are immaterial.

If needed, you can spare yourself the need to frequently write out the same absolute path by storing it in a variable:

(define postgres-path "/usr/local/bin/psql")

;; …

(define (awesome) ;; … (process postgres-path) ;; …
)

Second solution: Ensure that all GUI applications execute with a specific PATH

Another approach is to ensure that DrRacket, as well as every other application, is executed in an environment where PATH has a certain value. Think of this as hacking your .profile/.bashrc/.zshrc, but for graphical applications.

On macOS, the idea is to make sure that /etc/environment exists, with these suggested contents (and make sure that /etc/paths looks right). This approach is quite ugly, but works. (Make sure you log out and log back in!)

If you’re not on macOS, there’s probably a similar mechanism for ensuring that GUI applications have a certain environment.

Third solution: Set the PATH in your Racket program

A third solution is to explicitly manipulate the PATH environment variable in your Racket programs using putenv. Near the beginning of your program, try doing something like this:

(putenv "PATH" "/usr/local/bin:/usr/bin:/bin:/sbin")

(Suitably adapted to your case, of course.)

The third approach avoids the need to set a global PATH environment variable that GUI applications use (disadvantage of approach 2) and allows you to use shorthand names for programs psql instead of /usr/local/bin/psql (disadvantage of approach 1).