Most programming languages appear to be designed to not allow one to declare an identifier that starts with a number. I was just curious to know the reason. I have already searched the web, but couldn't find a satisfactory explanation.
-
4Do you have a single example of a variable name where it would have a benefit for clarity and readability?– SecureFeb 5, 2012 at 12:56
-
6@Secure: 3dspline, 4seasonPizza, 2pdfConverter, 8bitInt, ...– user unknownFeb 5, 2012 at 18:02
-
6Forth allows it. Of the built-ins: 2DUP, 2DROP, 2SWAP, 2>R, 2R@, 2R>, 0=, etc.– Peter MortensenFeb 5, 2012 at 18:23
-
as does TCL but I don't think any of the standard TCL commands start with a number– jk.Feb 6, 2012 at 11:49
-
1Possible same on SO: stackoverflow.com/questions/342152/…– Ciro Santilli OurBigBook.comApr 4, 2015 at 22:15
9 Answers
In C/C++, a number followed by a letter is considered to be a numeric constant and the string that follows, qualifies the type of the constant. So for example (these are VC++, not sure how standard they are):
- 0 - signed integer
- 0l - signed long integer
- 0u - unsigned integer
- 0i64 - 64 bit signed integer
So a) it is easier for the lexer as Daniel said but also b) it makes an explicit distinction since 0y might be a variable but 0u would never be. Plus other qualifiers, like "i64" were added way later than "l" or "u" and they want to keep the option open of adding more if needed.
-
8also, hex numbers are written in the form 0xd+ where d+ is 1 more hex digits 0-f -- so 0xbeef is a perfectly valid "number".– tcrosleyFeb 5, 2012 at 5:46
-
21you guys do realize I wasn't going for a language spec, but only provided few examples to illustrate the point, right?– DXMFeb 5, 2012 at 9:21
-
7Re: "they want to keep the option open of adding more if needed": And C++11 even lets you add your own; see http://en.wikipedia.org/wiki/C++11#User-defined_literals.– ruakhFeb 5, 2012 at 20:59
-
2I don't think this is the right explanation. The "identifier can't start with a digit" rule was true of Algol, Pascal, and other languages that did not allow alphabetic suffixes to numeric constants. Feb 7, 2012 at 22:56
-
1@LarryGritz: "Consistently separating words by spaces became a general custom about the tenth century A. D., and lasted until about 1957, when FORTRAN abandoned the practice." —Sun FORTRAN Reference Manual (from wiki). Fortran had it's own special reasons because they decided spaces in general were optional. MODERN languages like their whitespace. You are on your own with Algol but I'm not how modern that one is either. On the other hand C/C++/C#/F# all have suffixes.– DXMFeb 8, 2012 at 0:58
The convenience of the people implementing the lexer. (No, seriously, that is about it. Various languages have other reasons, but it ultimately comes down to that.)
-
2It would easy to distinguish between integral literals and identifiers starting with digits using PEGs or other modern parsing techniques. Even compilers using primitive lexers could put them in the same token category and differentiate later. It would just be very awkward if e.g.
0flu
was a literal and0glu
was a local identifier. Feb 5, 2012 at 7:24 -
2It is absolutely possible for people to distinguish them. The decision is made based on convenience (or, if you are less charitable, laziness) rather than technical requirements. Feb 5, 2012 at 7:58
-
2@DanielPittman: You'd need semantic analysis to do any sort of reliable disambiguation, so that can't be done in the lexer. Pushing the decision out of the lexer makes the parser more complex, and to what benefit? Besides the very poor cost/benefit situation, there is just no good way to handle a case like
int 0u = 5; unsigned int x = 0u;
However you choose to define the interpretation of this code (likely either x == 0 or x == 5), people are going to be confused because of the ambiguity. Even if it were trivial to implement the compiler this way, a good designer would likely not do it.– JorenFeb 5, 2012 at 10:14 -
12The main convenience is for the parser in my head, and not for the creator of the language. Feb 5, 2012 at 11:07
-
2It's still a surprise to many people to learn that lexical analysis is usually be a large factor the slowest stage of a compiler/interpreter. Feb 5, 2012 at 12:03
Consider the following 2 cases:
Case 1
Lets assume that an identifier could begin with a number.
So a statement like below would be valid (since an identifier can have 1 or more characters) :
int 3;
When I try to use the above variable in a program, it'll result in compiler ambiguity:
int 3,a;
3=5;
a=3;
In the statement a=3
what is the role of 3 (is it a variable with value 5 or is it the numeral 3) ?
Case 2
As opposed to the example above, lets assume that a language were to actually allow identifiers starting with a number while still disallowing numerals being used as identifiers. This can cause the following issues:
The language rules regarding the variable which says that a variable can consist of 1 or more characters will have to be redefined to a complex rule like: A variable can have one or more characters and must be unique if it does not start with a number while it cannot be of single character length when starting with a number (etc..)
The compiler will have to check for and report error cases when all numerals (eg 333) and valid alphabet suffixes (e.g. 34L) are being used as variable names. In loosely typed languages like Python and JS where you can use variables on the fly without declaring them, it may even be impossible to check for the special cases involving all numerals e.g.
if (33==5)
Here, 33 could be an erroneous undeclared variable that the user has declared. But the compiler will not be able to identify this and report the error.
Making this restriction will prevent the programmer from using numbers as identifier names.
-
3Under this logic, identifiers could not contain characters as they would be ambiguous to keywords. Can you imagine how disastrous
int char = float
would be?– PubbyFeb 5, 2012 at 13:21 -
4@Pubby : I don't see how you can extrapolate what I said to some utter non sense which I can't figure out yet. What does your comment mean?– aml90Feb 5, 2012 at 13:27
-
1I'm saying that you're taking the question too literally and that it's not at all ambiguous by using lexing precedence. For instance, how does the compiler know
int
is a keyword and not an identifier? Well,int
has higher precedence just as numical lexemes would have.– PubbyFeb 5, 2012 at 13:39 -
1Traditional scoping rules would say that the
3
ina=3
refers to the identifier. Anyway, I understand your point but disagree - let's leave it at that.– PubbyFeb 5, 2012 at 13:55 -
3I also find this argument weak. It would be trivial to write a lexer that would accept identifiers that start with, but are not entirely composed of, numerals. Feb 7, 2012 at 22:59
For the most part this has nothing to do with making it easy for the compiler writers and parsing efficiency, but, more to do with designing a syntax that encourages clear readable and unambiguous code.
Its the language designers thought it would be nice to be able to write numeric literals like the number 1 as just plain 1 .
It would be quite possible to design a language syntax where numeric literals were quoted in some way for example tildas so the numberic literal for number one was encoded as ~1~ and anything not a keyword and not enclosed in quotes was treated as a variable name.
So you could code statements like:
1 = ~2~
two = 1 * ~2~
But also:
2 = ~3~
six = 2 + 2
Whatever syntax you choose ambiguous and hard to follow code is unavoidable.
The C language and most of the "curly brackets" languages descended from C also thought it a good idea to allow programmers to code Octal and Hexadecimal literals directly, and, to specify the type of the literal if this was important. So
010 // Octal 10 = 8;
0x10 // Hexadecimal 10 = 16;
5l // long integer with decimal value 5
2.0d // double float with value 2
So even if you allowed variable names start with a number followed by a combination of numbers and letter that included at least one letter you would present the programmer with the problem of deciding whether a given group formed a variable name or a numeric literal so
2lll = 22 // OK
2ll = 2 // compiler error
Such ambiguity would not help anyone writing or reading a program.
For a closely related real world example you could look at PL/1 language whose designers thought that being able to use keywords as variable names was a good idea so that:
IF THEN THEN THEN = ELSE; ELSE ELSE = THEN;
IF IF THEN ELSE = IF; ELSE THEN = ELSE;
DO WHILE (WHILE = DO); END = WHILE + DO; END;
Is valid code which compiles and executes.
-
C was designed as portable assembly for Unix. Unix was originally designed for an 18-bit machine, where octal is a good fit for printing in the same way hex is a good fit for printing 8/16/32-bit machine values. Hence they actually needed octal.– user1249Feb 6, 2012 at 19:44
-
Also for bit twiddling (OR,XOR,AND,NOT) and implementing device drivers its important to specify the exact size of a literal as well as the value! Feb 7, 2012 at 1:33
Fortran had a huge effect on how later languages were designed. Early on (some of these problems have since been fixed) Fortran had almost no rules restricting what name you could give to an identifier. This made the language extremely difficult to parse both for compilers and for programmers. Here's one classic example:
if if .eq. then then = else else else = endif endif
K I K K I I K I I K
Here I've marked the "language key words" with K and the identifiers (variable names) I. Given that there's no difference in spelling, I think you can probably understand how confusing this could be. Of course, this is an extreme example, and it's unlikely anybody ever wrote code quite like this on purpose. Sometimes people did "recycle" language key words as identifier names though -- and in a lot of cases a simple typo could result in code that the language spec said should be parsed this way, even though it wasn't intended at all. For another well-known example, compare this:
do 10 i = 1,10
to this:
do 10 i = 1.10
The first is a do loop -- iterating a block of code 10 times. The second, however, has had the comma changed to a decimal point, so it's assigning the value 1.10
to a variable named do 10 i
.
This also meant that writing a Fortran parser was relatively difficult -- you couldn't be certain that the do
at the beginning of the line was really a key word until you reached the end of the line, and verified that all the other elements of a do
loop were present. The parser generally had to be ready to "backtrack", re-parsing the line from the beginning to come to the "correct" (but often unintended) answer of what was really there.
After a few years of this, language designers (most of them anyway) went toward the opposite extreme -- restricting nearly everything about the language as much as possible without the users complaining too much.
Early BASIC, for example, basically said you couldn't even use a key word as part of an identifier -- for example, fora=1
would be parsed as for a = 1
(i.e., the beginning of a for
loop, not an assignment). That apparently generated enough complaints that it didn't last very long. The rule about starting an identifier with a digit apparently hasn't generated a lot of complaints, so it continues to be used (at least in most languages).
-
IMHO this is closest to the real reason. Early languages such as Fortran were, in some ways, too unstructured, leading to difficulties writing robust compilers and difficulty for humans to correctly visually parse the source code. The "do10i=..." is a classic and famous example. As languages evolved, some of the rules were tightened up. Algol is probably the grandfather of the standard "identifiers start with letters and thereafter can have letters or numbers" rule of thumb. Feb 7, 2012 at 23:02
-
FYI, the Microsoft BASIC interpreter that formed the basis of most popular Microcomputer versions of BASIC (including Applesoft Basic and Commodore Basic) used a greedy tokenizer to convert any sequence of characters which matched a language token into a byte value with the high bit set. This was done without any syntactic analysis. Then, when running the program, the interpreter would assume any letters it found constituted part of a variable name.– supercatJul 11, 2012 at 17:11
Likely this convention has evolved from very early historical language design decisions, as on early machines the entire compiler, including lexical analysis, had to run in a few kWords, less memory than even just the first-level processor data cache on current mobile devices, so the allowed variable names were very limited, and had to be easy to distinguish from numeric constants in very few op codes.
Thus, the convention became what generations of programmers are used to.
It's not a logically required rule for programming language but just the convention used by many language designers.
I can design radically different language that allow all character for identifiers. For all code lines, first 20 character will describe the statement type then next 20 character will define the first symbol for the statement, and next 20 character is operand for the statement. This language will be executed on a stack processor.
01234567890123456789 01234567890123456789 01234567890123456789
decl symbol 12345
assign value 12345 12345
decl symbol 99999
assign value 99999 12345
push 12345
push 99999
add
print top
This code could be translated in C as below:
int i12345 = 12345;
int i99999 = 12345;
printf("%d", i12345+i9999);
That's all. It's meaningless and the no-number-in-identifiers rule is also pointless in logical ground.
In addition to "convenience for the lexer", I think it is also worth considering "convenience for the reader".
When reading code, you need to quickly and repeatedly identify which words are identifiers, and which are numbers. Looking for a digit at the beginning is easier on our visual pattern-matching; it would be a chore if we had to carefully check all the characters to make sure.
The answer to this question lies in automata or more precisely finite automata that defines the regular expression. The rule is...compilers need exact algorithms or rules to decide at every character that they parse. If identifiers were allowee to start with a number then the compiler will be in a fix..about the nature of the token coming...will it be a number or an identifier...and as compilers cant backtrack to earlier positions...so..to make it clear to the compiler that the coming token is precisely an identifier or a number...this restriction is there...coz of this...compiler knows just by scanning the first character that the coming token is an identifier or a number.