>> (with-current-directory "/some/dir"
>> (lambda () ...))
>>
>> saves the OS's idea of the current directory, does a chdir() to the desired directory before the thunk and then a chdir() back to the old one afterwards.
> I guess I’m missing the use case for this… Are you trying to design something that works only in single-threaded Schemes? In other words you are deliberately ignoring issues with concurrent uses of this construct because there’s no solution?
I didn't think of it that way, but your conclusion is right. I'm not
even quite sure what the appropriate use of per-thread OS working
directories in C/C++ programs is. I guess it can work if one is very
careful to be sure of which thread all code runs in (including library
code).
I haven't worked on many multi-threaded programs, and not any big ones,
but what I've done is to have the same OS cwd on each thread and merge
pathnames manually. For per-thread cwd's I'd do one of these:
1) The application programmer would have a per-thread variable or
parameter containing the thread's base pathname. This variable would not
affect the OS interface; the programmer would have to manually merge
pathnames to the base path before passing them onto the OS procedures.
2) Have a per-thread file descriptor that has opened that thread's
current directory. The file descriptor would be passed to Unix calls
like openat() and unlinkat() to help resolve relative paths.
Option 2 is nice in that we have support from the kernel, but it's not
as portable. I'm not sure whether there's a Windows equivalent. And it
would require an optional cwd argument in all the OS procedures (or the
per-thread fd would be stored in a standardized parameter as you
suggested, and the OS procedures would always get it from there without
needing a separate argument).
> What is the purpose of this construct? It sets up the current directory of the process (to affect the behavior of external, i.e. C and OS, code)? It implements the concept of “current directory” for Scheme code to use (for example to expand relative filenames)? Only one or both?
Both. Most languages' OS APIs don't resolve relative pathnames
themselves, they just pass them verbatim to the Unix syscalls so the
kernel resolves them. So the with-current-directory construct would
affect Unix syscalls, and thereby implicitly affect Scheme code by
virtue of the fact that Scheme code would defer to the syscalls.
The with-current-directory thing is provided because it's simple and
convenient; I haven't met an implementation in any language that tries
to do anything fancier than the chdir() and unwind-protect thing. So
users know what it does and don't expect anything more. This is the
first discussion I've been in where the idea of doing anything more
complex has been brought up. I think this kind of thoroughness is a
merit to the Scheme community and the Right Thing design approach.