Expanding File Names in Guile

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.

This entry was posted in Programming and tagged . Bookmark the permalink.