brian@granite.jpl.nasa.gov (Brian of ASTD-CP) (05/08/90)
I would like to start a discussion concer- ning the Scheme essential procedure "load". The Scheme definition, R3.99RRS, states that the re- turn value of "load" is unspecified. However, I think that "load" should have a specified return value, and the next few paragraphs explain why I think so. With many implementations of Scheme, load returns #f when the file passed to it is not found, and #t otherwise. I think this ought to be the standard behavior of load. In some newer, more "Graphical User Interface"-oriented implementations of Scheme, load presents an interactive dialog, asking the user to pick a file from a list when the given file is not found. With this behavior, load is not permitted to return #f and load cannot be used in unattended programs when it might return #f. However, there is a very good reason why one might want load to tell a program that it can't find a file. Imagine that a collection of software is divided into modules and clients. The modules are reusable abstract data types, procedures, libraries, etc. The clients are programs built up from the modules. It is highly advantageous to maintain the modules separately from the clients. In particular, clients should not require knowledge of the directories modules reside in. Clients should simply refer to modules by name, as in (load-module "methods.sch") (load-module "queues.sch") (load-module "binary-search.sch") If clients use only load, then they must have hard-coded directory names, as in: (load "MyDisk:Scheme Code:Modules:OOP:methods.sch") (load "MyDisk:Scheme Code:Modules:OOP:classes:queues.sch") (load "MyDisk:Scheme Code:Modules:Procedures:binary-search.sch") and so on. The big maintenance problem occurs when a module is moved. Using load-module, no clients need to be changed. Using load, however, all clients of a module must be manually tracked down and changed. The implementation of load-module is simple. It searches a canonical list of module directories, using the return value of load to decide whether to continue or terminate the search. (define (load-module filename) (let iter ((canon-dir-list (list "MyDisk:Scheme Code:Modules:" "MyDisk:Scheme Code:Modules:OOP:" "MyDisk:Scheme Code:Modules:OOP:classes:" "MyDisk:Scheme Code:Modules:Procedures:" ))) (cond ( (null? canon-dir-list) #f ) ( (load (string-append (car canon-dir-list) file)) ) ( else (iter (cdr canon-dir-list)) )))) (my real load-module is a little different because it avoids loading a module more than once, but the version above illustrates the use of load in load-module.) With load-module, if a module directory is changed, only the canonical directory list, which is in one place in an initialization file, need be changed. All static data concerning module locations is hidden in this single list, rather than being distributed in multiple copies among the clients, which is clearly a much inferior organization. It should be clear that load-module relies on load's returning #f when a file is not found in a directory. The ostensibly more user friendly dialog behavior of load actually forces a developer of Scheme code to violate separation of module and client and to pollute client code with extrinsic information that creates a maintenance headache. Comments? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Brian Beckman . . . . . . . . . . brian@granite.jpl.nasa.gov. . . . . . meta-disclaimer: every statement in this message is false . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
kend@tekchips.LABS.TEK.COM (Ken Dickey) (05/08/90)
In article <3601@jato.Jpl.Nasa.Gov> brian@granite.Jpl.Nasa.Gov (Brian of ASTD-CP) writes: ... >With many implementations of Scheme, load returns >#f when the file passed to it is not found, and #t >otherwise. I think this ought to be the standard >behavior of load. ... It is certainly desireable to have a portably standard behavior here. It is also somewhat difficult to achieve. In some implementations of Scheme, load acts like BEGIN in that it returns the last value of the storage loaded. Aside from returning some possibly useful value directly, I think that there are 2 kinds of information which could be returned: [A] Was the file/object/whatever found or not. [B] Was the file/object/whatever successfully loaded, partially loaded, or not loaded? Once error conditions are considered, the question of what LOAD returns becomes more complex--complex enough that there has not yet evolved a concensus for a standard Scheme error system [I would certainly like one!]. I believe that it will be difficult to resolve the question of what LOAD should return in the absence of a standard interface for failure/error handling. What if there is no error handling mechanism? Also to be considered are behaviors which differ in loading compiled vs source files. Possibilities suggested: Have [possibly optional] success and failure continuations passed in, Call an `system' error handler in a specified way, Pass in a function to be called on error, etc. Adding parameters, however, gets into parameter order and issues of first-class environments and whether LOAD takes an environment parameter. Given that LOAD is very system dependent, the prudent approach is to hide it abstractly away somewhere with other system dependent code. -Ken
net@tub.UUCP (Oliver Laumann) (05/08/90)
In article <3601@jato.Jpl.Nasa.Gov> brian@granite.Jpl.Nasa.Gov (Brian of ASTD-CP) writes: > I would like to start a discussion concerning the Scheme essential > procedure "load". The Scheme definition, R3.99RRS, states that the > return value of "load" is unspecified. However, I think that "load" > should have a specified return value [...] I don't think "load" should have a return value which indicates success or error. When the file could not be opened or some other error condition occurred, it should simply "signal an error" (whatever this means) like all other primitive procedures do in case of an error condition. After all, "write" doesn't return #f either when a write error occurred (disk full or whatever). In addition, Scheme implementations could provide an "(autoload 'foo)" function which causes a file "foo.scm" or so to be loaded automatically the next time the symbol foo is evaluated but not bound. In this case one wouldn't be able to test the return value of the call to "load" anyway. I'm sure most Scheme implementations allow you to catch and handle an error anyway, for instance by "hooking" into the "system error handler" or by redefining the error handler. In Elk, for instance, one can easily write an equivalent to the Lisp "errset" which can be wrapped around the call to "load" to test whether it failed. > It is highly advantageous to maintain the modules separately from the > clients. In particular, clients should not require knowledge of the > directories modules reside in. Clients should simply refer to modules > by name, as in (load-module "methods.sch") [...] > The implementation of load-module is simple. It searches a canonical > list of module directories, using the return value of load to decide > whether to continue or terminate the search. I think this can be handled by the more general concept of a "load path". Some (many?) Scheme and Lisp implementations maintain a list of directories that are searched for the file that is specified in the call to "load". In Elk, for instance, "load-path" is a variable bound in the initial environment; the initial value is a list of directories where the Scheme system files and extensions can be found plus the current directory. Applications can bind the "load-path" to a different list of directories or add the directories where the applications' modules reside. -- Oliver Laumann, Technical University of Berlin, Germany. pyramid!tub!net net@TUB.BITNET net@tub.cs.tu-berlin.de
dorai@titan.rice.edu (Dorai Sitaram) (05/08/90)
In article <3601@jato.Jpl.Nasa.Gov> brian@granite.Jpl.Nasa.Gov (Brian of ASTD-CP) writes: >I would like to start a discussion concer- >ning the Scheme essential procedure "load". The >Scheme definition, R3.99RRS, states that the re- >turn value of "load" is unspecified. However, I >[...] >With many implementations of Scheme, load returns >#f when the file passed to it is not found, and #t >otherwise. I think this ought to be the standard >behavior of load. [...] Wouldn't you be satisfied with just `file-exists?'? Almost every Scheme I've come across provides this procedure -- though RRRS doesn't include it. Obviously, `file-exists?' and the unspecified-returning `load' provide the capability you desire (load-module, etc.). (define load1 (lambda (f) (if (file-exists? f) (begin (load f) #t) #f))) (NB: The #f/#t `load' doesn't make `file-exists?' superfluous. The latter provides the ability to say that a file exists without having to load it too.) --dorai
shap@thebeach.wpd.sgi.com (Jonathan Shapiro) (05/10/90)
What file-exists *doesn't* do is guarantee that a subsequent load will succeed, because the file could get deleted. The underlying issue here is that the scheme designers haven't yet agreed on an error handling mechanism. It would be unambiguously wrong to specify one in the standard if it isn't right. Jonathan Shapiro Silicon Graphics, Inc.