13

Historically, interpreters and compilers have limited the length of identifiers. For example, FORTRAN I and II considered only six characters to be significant, and LISP 1.5 forbid symbol names to exceed thirty characters (although the infrastructure for storing the names would have allowed for arbitrary lengths).

What was the first system, i.e., an implementation of a programming language*, not to place such limitations? The only factor constraining identifier length should be the available memory on the computer, modulo overhead for the compiler's coding, the run-time environment, and so on. All characters in an identifier must be significant.

According to a note by Arthur Sale in the sixth Pascal Newsletter (on page 46), the Pascal compiler for the Burroughs B6700 was one such system by 1976:

Another [issue in program portability], not so obvious, is the practice of allowing any-length identifiers, but permitting only the first few n characters to be significant ignoring the tail. If a program is compiled on a computer with true any-length identifiers (e.g. B6700), then it is quite on the cards that it will be compiled incorrectly by some other computer without warning, as if the names are TEMPATTOPOFKILN, TEMPATTOPOFFLUE. No, identifiers must be true any-length, or fixed up to a length n with at the very least a mandatory warning or better an error thereafter.


* Language specifications often leave details about identifier length up to the implementation. For example, Pascal requires at least eight characters to be significant (case-insensitively). Algol says nothing, giving only the production rule for <identifier>, which of course generates an infinite number of strings.

C has complicated rules regarding identifiers, where the nature of a declaration can determine the minimum number of significant characters. Because of the desire to maintain compatibility with existing systems on which C must run, C89 retained the obnoxious old FORTRAN-like rule that only six characters are significant in “external names”. The C standard rationale says

The decision to extend significance to 31 characters for internal names was made with little opposition, but the decision to retain the old six-character case-insensitive restriction on significance of external names was most painful. While strong sentiment was expressed for making C “right” by requiring longer names everywhere, the Committee recognized that the language must, for years to come, coexist with other languages and with older assemblers and linkers. Rather than undermine support for the Standard, the severe restrictions have been retained.

The rationale mentions that the distinction between internal and external names is “obsolescent”, but the most recent draft has not removed it (subclause 5.2.4.1).

In contrast, the Common Lisp standard implicitly requires identifiers to be unconstrained. Identifiers are effectively first-class entities in Lisp (known as symbols); one attribute of a symbol is its name, which can be any string. Equivalence of symbol names is determined by a typical comparison of strings. (For historical reasons, case conversion is applied by default, although this can be changed.) See Chapter 2, “Syntax”, for details. I would not be surprised if a Lisp implementation is the answer to this question, given that symbols were often used for strings in older dialects of the language; e.g., you might see (PRINT '|PROCESS ENDED|) or (PRINT 'PROCESS/ ENDED) in Maclisp.

14
  • 2
    A good question would be if an system existed which didn't care about the length of identifiers but relied on the strength of its hash, at a cost of having to resort to circumlocutions in error messages ("Identifer in line N, pos M had been already declared in line K, pos L"). FWIW, a good hash on a mainframe with word size greater than 32 could have allowed to have identifiers of virtually arbitrary length with minuscule chance of collisions. Alas, such hashes had not been invented then.
    – Leo B.
    Jun 9, 2021 at 8:39
  • 4
    @ThorbjørnRavnAndersen Yes, at least in the case of FORTRAN (six-bit characters, thirty-six-bit words). See also this answer at TeX.SE.
    – texdr.aft
    Jun 9, 2021 at 9:34
  • 3
    Given that all compyilers run on a computer with finite storage capacity, there is an implicit upper limit anyway. Jun 9, 2021 at 17:43
  • 4
    From a practical standpoint, using a statically allocated buffer to hold the most recently received identifier long enough to perform a table lookup will often be significantly faster than trying to use a dynamically allocated buffer for that purpose. Allocating one fixed buffer of length e.g. 64 bytes might be reasonable even if most identifiers would be much shorter, but allocating a 4095-byte buffer for such purpose when few identifiers are even 40 characters long would have been seen as wasteful.
    – supercat
    Jun 9, 2021 at 21:48
  • 3
    @Raffzahn I contest the idea that the question was ever about languages, rather than implementations. (Although I am biased, since I am the author of the question.) The point of the additions about standards etc. was simply to add discussion of specifications, not to broaden the scope of the question; they can be removed if you think that would improve things.
    – texdr.aft
    Jun 10, 2021 at 16:37

3 Answers 3

12

My nominee is SNOBOL4 (1962 onwards).

I can't see any mention of limits in online versions of the green book, but I'll check my paper copy later. For now, I'm assuming MACRO SPITBOL is representative of the language itself.

We have to distinguish between natural variables, where the name literally appears in the source text in the conventional manner, and other variables. This is not a matter considered in the original question!

SNOBOL4 is essentially a line-oriented programming language, being born in a card-based world. A line can extend over multiple cards, but not indefinitely.

For natural variables, the name must fit on one line. The line length limit is 1024 characters; in practice, you'll want an actual statement, so the practical limit is a bit less. The name is subject to the usual sort of constraints: letters, numbers, underscore, must start with a letter.

Unnatural variables (I made up this term) can have any string as a name. Such variables are created by indirection: X = '!$@#' ; $X = 42. Such names are limited only by the maximum supported string length, which in contemporary implementations is 4M chars; I'd have to consult paper to see what it was in older versions, but it was surely less: for example, the address space of a DEC-10 job was 256 KW = 1.25M chars.


And now I've checked the paper:

MACRO SPITBOL has (or had; I'm not in touch with current versions) a design limit; it was necessary for the maximum size of any object to be less than the lowest heap address. This max size was adjustable at compile time in the two implementations I used. For ICL 1900, per Leeds University Technical Report 90, the default was "about 15000". For DECsystem 10, Tech Report 94, the default was "about 2600".

(This has more levels than we previously discussed. There's the language, basically SNOBOL4. Then there's the implementation, MACRO SPITBOL. And then there's the implementation of the implementation, which consists of translating the MINIMAL source to the target assembly language, and supplying an appropriate OS interface. And then there are runtime arguments, which in this case directly affect the permitted length of names!)

SNOBOL4 on IBM S/360, as reported in the green book, had a max string length of 5000 characters by default. Again, alterable at run time.

This is all very ambiguous in terms of an answer to the question: there is no inherent limit on the length of names as such, but there are other 'environmental' limits which are lower than the limit caused by finite memory.

2
  • We might have a winner. As for the existence of "unnatural" variables, I think the reference to Lisp implies that they should count. Also, in Common Lisp we can do (let ((x "!$@#")) (setf (symbol-value (intern x)) 42)), although the assignment affects only the global value of !$@#, not any lexical binding. (But I'm guessing this distinction is not present in SNOBOL.)
    – texdr.aft
    Jun 9, 2021 at 22:53
  • 1
    The honor of being the first implementation of a "conventional" language (i.e., one without first-class identifiers) that meets the requirements of my question probably goes to some Algol compiler (such as this masterpiece).
    – texdr.aft
    Jun 9, 2021 at 23:10
1

I would think that ALGOL might be a good candidate, as the language definition did not limit identifiers in length (*1). This might of course not be true for every real implementation.


At that point it might be hard to draw a line what consists a machine/memory limitation or not. After all, identifiers go into tables. Restricting their length is a valid measure to get that table into memory. Isn't it? Machines with just a few to a few dozend KiB were the norm in times of these early languages, so allowing arbitrary size sees more of an ivory tower POV when it comes to implementation.

In addition, language and environment must be seen as separate entities: Definitions of languages may be valid within that language, but as soon as it's about a real world implementation, restrictions are no longer set by that language, but by the environment.

Usually an OS sets limits for its libraries and thus linker(s) handling them. So validity of identifiers outside a single compilation unit is not defined by the language or its implementation but by the environment (OS) it it is used/produces code for.

Long story short: A useful answer can only be given either

  • for a language, or
  • for an environment (OS) - in which case it is language independent

In the first case it will be independent of OS and thus implementation, so purely about the language, while the second is strictly language independent. Since an environment is always a superset of a language implementation, any answer including that will not be about the language or its implementation, but the environment of that i


Thinking of it, arbitrary identifier length may as well be true for FORTRAN.


*1 - In fact, it didn't put much limitations on identifiers anyway, as it not only allows spaces within (something that, going by its syntax, C could have inherited), but effectively handled them as a different namespace from keywords (IIRC)

12
  • 2
    Perhaps I should have made it clearer that I was interested more in implementation than in specification. Incidentally, I think Common Lisp is the one language to get identifiers "right": Any characters are allowed, but you must escape those that would otherwise act as delimiters to the parser (spaces, parentheses, and some punctuation). And of course all characters are significant and there is no limit on the length.
    – texdr.aft
    Jun 9, 2021 at 1:46
  • 2
    In my experience, Algol 60 compilers did not 'allow' spaces in identifiers, they simply ignored all spaces in source code (including, annoyingly, in quoted-strings; you had to use the space-symbol, represented by ␣ in the reference language for spaces in, e.g., printed output.) Thus line count and linecount were the same identifier. Jun 9, 2021 at 3:08
  • 2
    In the early versions of Fortran, blanks were only significant in character (Hollerith) constants. So "DO 10K=1,2" was parsed as a DO loop "DO 10 K = 1, 2" but "DO 10K=1" was parsed as an assignment statement "DO10K = 1".
    – alephzero
    Jun 9, 2021 at 11:04
  • 2
    Blanks definitely ignored in FORTRAN. I had to write D O 1 0 K = 1 , 2 since we were too poor to pay for odd-numbered columns. (I used one of these and mailed in my program decks). Jun 9, 2021 at 11:34
  • 2
    @alephzero: More fun, DO 10 K=1.2 would create a variable called DO10K and assign the value 1.2 to it.
    – supercat
    Jun 9, 2021 at 20:10
1

BCPL (first implemented by Martin Richards of the University of Cambridge in 1967) had the feature that identifiers were stored in hash tables, unlike Pascal which generally stored identifiers in a 60 or 64 bit word. The latter method usually used the 6-bit version of ASCII to pack in more characters. Anything earlier than that was most definitely before my time.

3
  • 2
    I'd suppose that many compilers stored identifiers in hash tables, the better to look up properties during compilation. Additionally, hash table storage seems to have no direct relation to any limit on identifier length. Could you add a few words of explanation? Are you saying that only the hashcode was retained, not the identiifer - such that two identifiers that hashed to the same code would be considered to be the same variable? Jul 20 at 1:03
  • No, I'm not saying that. The symbol table is referenced through a hash table whose entries point to linked lists of nodes containing identifiers. You might find this historical document interesting: core.ac.uk/download/pdf/35471178.pdf
    – jrrk
    Aug 2 at 8:56
  • I'll read it later (thanks) but that sounds like a standard method to hold identifiers (or any text string). Hash the identifier, hash bucket is a linked list to cater for collisions. Aug 2 at 11:58

You must log in to answer this question.

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