Why is it that languages like C/C++/Go and others have

import-keyword "libraryName.fileExtension"

When something like

import-keyword libraryName

Has been shown to work fine?

  • 3
    $\begingroup$ I think this is confusing the use of string syntax in C/C++ for includes with the actual use of strings, as in IIRC JavaScript. It's worth mentioning the existence of angle bracket includes in C++, with somewhat different semantics, and I suspect this is ultimately a historical question. $\endgroup$ Commented May 17, 2023 at 0:35
  • $\begingroup$ Is this question about importing libraries (as per the example's content), or files (as per its syntax), or other things?  (For example, Java and similar languages import symbols into the current scope, and so neither need nor use any delimiters.  But that clearly can't work for filenames.) $\endgroup$
    – gidds
    Commented May 14 at 17:06

5 Answers 5


Usually you can have whitespaces in:

  • String literals
  • File names
  • URLs (well, if you escape them)

While you cannot have whitespaces in:

  • Identifiers

So if you want to import from whatever file, using string literals is a good idea compared to an identifier. Also, identifiers usually have something to do with variable/type/function names, so if you have identifier-based module names, then it's a good idea to use identifiers instead.


Extensibility over time

One reason for doing this is to allow a more extensible range of imports over time or across platforms. New behaviour can be added to the interpretation of the string later on without changing the fundamental syntax, so existing parsers continue to work.

For example, a later addition of language-level version numbering to imports would require a syntactic change in Python (which uses the import libraryname format) but not in JavaScript modules, which use host-defined strings. Similarly, relative (or absolute) imports could be added simply by reusing the string.

Imports that aren't code

Another possibility is that you may be able to import files that aren't code libraries at all: perhaps import "logo.jpg" as logo : Image makes sense in this context, or import "documentation.txt" or import "https://example.com/data.json" . Resource imports like this could be quite powerful, but they do require being able to identify the location of the resource precisely.

You might even be able to import things that aren't files at all: perhaps a database connection or web service, in the manner of F# type providers. For reference examples of this sort of thing I'll point to (cough) this distant paper of mine and chapter 5 of my thesis, which discussed a few different options. There have been further developments in this vein over the last ten years.

It's not obviously good to overload the import system in this way, but it's not inherently bad either. Explicit imports can invoke static checking and resolution in a way that dynamic loading of data at run time can't, can be interpreted by packaging systems, and can offer uniform access to local, generated, and remote resources.

Identifier limitations

Strings may also encode values that aren't permitted in identifiers within the language, from odd characters in local file paths to URLs or beyond. The interpretation of the string is left up to the implementation.

It's not unreasonable for a language to project its identifier rules onto module names and define a fixed correspondence between them, but it's not necessary to do so always. In some cases, like for JavaScript, importing from arbitrary URLs is an expected need for real users, so a format that allows for that makes sense.

None of these are the reason for C (though in part perhaps for Go), which is how it is for historical reasons of its early days. New languages, and new import systems like JavaScript's, are more likely to follow one of the other reasons.


The C Preprocessor

C's preprocessor simply looks for a file with that name and copy-pastes its contents. Strings allow your filenames to contain whatever characters and have whatever extension without confusing the preprocessor. Furthermore, the delimiter used ("" vs <>) tells it where to look.

Symbolic imports, like those used by Java, weren't as much of a proven thing when C was designed (over 50 years ago!). Besides, they rely on enforcing filenames and in some cases folder structure.


Importing without built-in syntax

In some languages like Ruby, Lua and Zig, there is no special syntax for imports. Instead, importing is done using a regular function. Since it's a regular function, it has to take the name as a string, not as an identifier.


Import paths not in strings may be invalid/illegal syntax in the language.

Go (one of your mentioned languages) allows URLs in its import paths. If we had a "bare" import like import example.com/example-mod/packagename, this would yield a whole slew of syntax errors, because (usually) URLs as-is are not syntactically legal Go.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .