I really like Guile but one very annoying thing about it is that it doesn’t expand file paths. If you want to load a file outside the current directory you have to specify the complete path. Emacs has a very helpful function, expand-file-name
, that takes care of expanding file names. For example,
(expand-file-name "~/scheme/somefile.scm")
results in
/Users/jcs/scheme/somefile.scm
I wanted something similar for Guile so I added my own version, loosely modeled on the Emacs function, to my .guile
file
(define expand-file (lambda (f) (cond ((char=? (string-ref f 0) #\/) f) ((string=? (substring f 0 2) "~/") (let ((prefix (passwd:dir (getpwuid (geteuid))))) (string-append prefix (substring f 1 (string-length f))))) ((char=? (string-ref f 0) #\~) (let* ((user-end (string-index f #\/)) (user (substring f 1 user-end)) (prefix (passwd:dir (getpwnam user)))) (string-append prefix (substring f user-end (string-length f))))) (else (string-append (getcwd) "/" f)))))
I use this in conjunction with another function, require-file
, that loads the file if it’s not already loaded. The require-file
function lives in my .guile
too.
;; load a file unless it's already loaded (define loaded-files '()) (define require-file (lambda (f) (let* ((ffn (expand-file f)) (fsym (string->symbol ffn))) (if (not (memq fsym loaded-files)) (begin (load ffn) (set! loaded-files (cons fsym loaded-files)))))))
Now I can simply write something like
(require-file "~/scheme/lib/some-file.scm")
and have it loaded without worrying about specifying the full path name. This is especially handy when I’m using Guile interactively at the REPL. The checking if a file is already loaded is probably overkill but it seemed like a good idea when I wrote the code.