• No results found

Resource imports

5.5 Extensions and future work

5.5.3 Resource imports

As described so far, it is possible to write

import"path/to/module"asmod

to get a Grace object containing the top-level definitions from the given module, bound to the name "mod". Given that this is “just” an object, there is no reason it necessarily has to arise from Grace source code. We have already described the ability to import native code conforming to the appropriate ABI, for example, in Section5.5.1.

Given this existing mechanism, it would also be useful to access other resources through it. In Grace, everything is an object, so the representation of, say, an image, is naturally an object. Because the implementation of objects is opaque, we cannot know what is in it or where it came from

unless we wrote the object constructor ourselves. We could access these through the import mechanism.

We can again interpret the import path in a new way, as referring to a piece of data in some known file format, which can be presented as an object. Such data might include help text, which would be represented as a string, or a PNG image to be used as an icon, which would be represented as an image object; we call these outside pieces of data resources. Resources are not part of the source code directly, but are imported from the outside. To do so we need to know three things: that what we are importing is a resource, what kind of resource it is, and how to find it. We can resolve the last through the ordinary import lookup (and the package manager described in Section5.4). The others are more complex.

There are two conventional and widely-deployed2methods for deter- mining a file type: by the file name, especially a particular part called an extension, and by “magic number”3 inspection of the file contents. We considered both of these approaches.

Magic number inspection is fraught and prone to failure, as well as requiring incorporating a database of identifying features into the compiler. We considered this undesirable, particularly when we wanted to work across varying platforms.

Using the file extension ties the interpretation of the string to a particular filename layout, and requires we map these extensions onto file handlers somehow. We found this less objectionable, but worked to eliminate the rough edges where possible.

Our ultimate design draws from the “extension” mechanism. As import strings are (overall) uninterpreted, we must define what we consider an extension to be. We did not wish to eliminate already-working import

2We dismiss out-of-band metadata such as Macintosh OSTypes and Apple UTIs for

this reason, as well as HTTP’s and MIME’s Content-Type header, as they are not available on all platforms.

3A “magic number” is a specific sequence of bytes known to be at a particular location

in a file of a given type. The term originally referred to the first two bytes of a file on Unix systems, but now has the broader meaning given.

statements from continuing to function.

To do so, we instate a syntactic restriction on the import paths of Grace modules: the given import path may not contain a “.” character (the Unicode character U+002e FULL STOP) after the final occurrence of “/” (U+002f SOLIDUS). Remember that, when importing a module, we do not include ".grace" in the name; this restriction only prohibits importing from a path implying a source file name like “foo.bar.grace”.

If such a character does appear, this import statement is a resource import. A resource import still binds the given name to an object identified by the given import path; the difference is that the meaning of the import path may be interpreted by other code.

What code is that? It is determined by the part of the name following the “.”: in essence, the file extension determines an import handler to be used. If a module contains the import statement:

import"my/tool/logo.png"aslogo

then the"png"import handler is handed the path"my/tool/logo.png", as a string, and returns an object to be bound tologo. This object can be anything the handler likes, but is probably some representation of an image in this case. If instead the module contains

import"my/tool/licence.txt"aslicence then licence is likely to be aString.

How are these extensions mapped to handlers? A per-program registry is defined, and user code can add entries to it. When using the “gtk” mod- ule, for example, it may extend the registry with an entry mapping the “png” extension to something creating a GTK+ image object. Other mod-

ules may provide their own definitions. The system may predefine some handlers, like “txt” as a String mapping, but these could be overridden.

What if we assume a Smalltalk-style programming interface, with a closed world, and no “files”? Again, as the interpretation of the path is defined in user code, it can be mapped according to whatever approach makes sense in that system. The extensions are simply to identify an

interpretation in that case.

What advantages does this have? We can access resources, like images, documentation, or sound files through a single consistent interface. It fits entirely within the existing semantics of the language, and also provides a logical location to store relevant resources adjacent to the code that requires them, and even to distribute them in the same way as code through the package manager described in Section 5.4. We extended our package manager to support resource imports, which it treats simply as opaque files to retrieve and place in the correct directory structure for the ordinary import mechanism to handle. The meaning ofimport"logo.png"aslogo is likely to be clear to the reader, even if they are unfamiliar with other code. The disadvantage is that there is an added point of complexity in the import system, and that a class of otherwise-valid filenames is excluded from use as a module name.

There are some drawbacks to this design: firstly, we have introduced a global registry, which inherently creates the potential for side effects at a distance and the potential of conflicts. Secondly, typing is an issue; because resource imports are handled at runtime we may not be able to typecheck them statically. Gradual typing addresses many of the typing issues. Finally, we have given an undesirable semantic importance to file extensions; the “morally correct” way to represent file types is probably by MIME type (now Internet Media Type [55]), but doing so is challenging on most platforms, and some file extensions may correspond to multiple types. The latter problem can be mitigated by defining “false” extensions, which do correspond directly to a type and mangle the import path appropriately.