7
$\begingroup$

Preamble

As you probably know, keywords are special reserved words in a programming language that have specific meanings and purposes and can’t be used for anything but those specific purposes.

An example of a keyword in Python 3 is False, which you cannot use as a variable at all:

>>> False = ''
  File "<stdin>", line 1
    False = ''
    ^^^^^
SyntaxError: cannot assign to False

Built-ins could for example be a built-in function - for example, again from Python 3, the function open.

open can actually be used as a variable - in which case you can no longer use it with its intended function until you remove it from scope:

>>> open = 'hey' # we've overwritten open as our own variable in the current scope
>>> open('file')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
>>> del open
>>> open('') # open is now back as a built-in
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: ''

Variables colliding with keywords and built-ins could lead to the following:

  1. They could potentially remove words from the programmers vocabulary (examples in Python are file and hash)
  2. They could potentially add confusion, bugs, and errors when programmers are allowed to overwrite them.
  3. The IDE of the developer will color and potentially flag something as a keyword where the developer intended otherwise.

And likely more, feel free to add.

So, what options do we have to avoid collisions?

$\endgroup$
6
  • 1
    $\begingroup$ The id function in python is particularly annoying since it's common to want to name a variable that and the function itself is not very useful $\endgroup$
    – mousetail
    May 23, 2023 at 10:40
  • 2
    $\begingroup$ can you please not put answers in the question post and please not lead the question in the question post? $\endgroup$
    – starball
    May 23, 2023 at 20:53
  • 3
    $\begingroup$ @starball Part of the StackExchange model is to put information about what you've already tried, and what research you've already done, in the question. I don't think it's a problem. See How to Ask $\endgroup$
    – pxeger
    May 24, 2023 at 9:31
  • 1
    $\begingroup$ By far the most popular option is simply to forbid programmers to use the reserved words for anything but their defined purpose. $\endgroup$
    – Mark
    Jun 1, 2023 at 20:33
  • 1
    $\begingroup$ @Mark Which leads to breaking changes when you add new keywords to the language. E.g. rank in MySQL became a reserved word when window functions were implemented. $\endgroup$
    – Barmar
    Aug 21, 2023 at 16:58

9 Answers 9

11
$\begingroup$

Kotlin and Swift, and probably others, allow keywords to be "escaped" with backticks, like `if`. Rust has the equivalent syntax r#if (the r stands for "raw").

$\endgroup$
4
  • 1
    $\begingroup$ There is a RFC currently being considered to allow k#if for keywords. This would allow you to use newer keywords in older rust editions before they become reserved $\endgroup$
    – mousetail
    May 23, 2023 at 12:30
  • 1
    $\begingroup$ In C#, the syntax @if can be used to "escape" keywords when used as identifiers. $\endgroup$
    – kaya3
    Aug 20, 2023 at 13:51
  • 1
    $\begingroup$ SQL's escape mechanism is similar, allowing table/column names that would otherwise be keywords. $\endgroup$ Aug 20, 2023 at 13:52
  • $\begingroup$ Note that Rust is about raw identifiers, that is r#if is used when you want a variable called if despite if being a keyword. $\endgroup$ Sep 6, 2023 at 15:02
11
$\begingroup$

For builtin functions/variables, (but not syntactically special keywords), some languages let you "opt out" of having them implicitly in scope, with a special import statement at the top of the file.

For example, in Haskell:

import Prelude hiding (head)
-- now you're free to define your own function called head
head = 123
main = print head

Try it online

and in Elixir:

import Kernel, except: [if: 2]

if = 123
IO.puts(if)

Try it online

This is particularly useful in Elixir, because a lot of what you might expect to be keywords are in fact implemented as macros in the standard library (although Elixir does still have reserved words and special forms, which, as far as I know, can't be excluded)

What syntax exactly you choose for this will depend on how you want it to fit in with your usual import syntax. Or maybe you could have a more special declaration that's unrelated to imports, like @no_builtin("head"); or something.

$\endgroup$
7
$\begingroup$

APL uses four techniques:

  • The core language is entirely symbolic, meaning that the most common operations do not need any alphabetical names at all. Not only do all mathematical built-ins have glyphs, even things like length, reverse, stringify, eval and table lookups have symbols.

  • Things that occupy normal syntactic roles (arrays, functions, and high-order functions) are prefixed with a "quad" () which is a stylised "box" referring to the box, or system, you're working on. These are called system names. E.g. ⎕NULL is a system constant, ⎕CT is the system variable for Comparison Tolerance, ⎕MKDIR is a system function, and ⎕S is the PCRE higher-order function for Searching.

  • Control structures and various declarations are prefixed with colon (:), indicating their kinship with labels, which are normal identifiers but are separated from the rest of the code line with a colon. E.g. :If begins a conditional and :Access public shared is used in object oriented programming.

  • Built-in APL objects have names, properties, methods, and events. While programmer-defined object members are directly referenced using this.that syntax, the built-ins are by default only reachable through dedicated system functions. E.g. one creates a new Timer object using the string 'Timer' (e.g. ⎕NEW 'Timer' 5000) and one can thereafter use the ⎕WG system function to Get the value of a property (e.g. ⎕WG 'Interval') and the ⎕WS system function to Set a property (e.g. ⎕WS 'Interval' 1000). If one needs frequent access to members of a particular object and doesn't need to put custom members there, one can set the ⎕WX system variable to eXpose the member names directly, thus occupying certain names. Similarly, events can be put on the queue using the ⎕NQ ("eNQueue") system function, and pending events can be handled using the ⎕DQ ("DeQueue") system function.

This has the, in my opinion, huge benefit that all identifiers are (by default) freely available to the programmer, with no need to remember the reserved words. At the same time, the language developers can always add more system names, keywords, built-in objects, and built-in object members without risking collision with any existing code.

$\endgroup$
6
  • $\begingroup$ This seems to work basically the same as PHP's $ except it's the opposite, functions and keywords are prefixed while variables are not $\endgroup$
    – mousetail
    May 23, 2023 at 10:57
  • $\begingroup$ @mousetail Maybe I should add that good APL code shouldn't contain many of the prefixed names, providing for a cleaner look than had it been the user-defined names that were prefixed. $\endgroup$
    – Adám
    May 23, 2023 at 11:03
  • $\begingroup$ So you are saying prefixing is optional in case there are no variables with the same names? $\endgroup$
    – mousetail
    May 23, 2023 at 11:06
  • 1
    $\begingroup$ @mousetail No, the prefixed names always need prefixing. However, one can activate direct access to built-in members of built-in objects, so it isn't necessary to use a system function to access them. $\endgroup$
    – Adám
    May 23, 2023 at 11:11
  • $\begingroup$ You forgot to mention the fact that most of the commonly used subset of APL is symbolic, so there's even less chance (not that there was any) of collision $\endgroup$
    – Seggan
    Jun 1, 2023 at 21:02
6
$\begingroup$

Common Lisp's package system is one solution: make all built-ins members of a predefined namespace that is imported in all modules by default.

So for example the word if is likely a built-in special form. So make it synonymous with a symbol named if from a predefined package, like std::if. That a program can define its own symbol (say user::if) if it is really necessary, then predefine a hierarchy of namespaces to import from, so that if there is a user-defined user::if then it is chosen in preference to std::if, if the current file spells it simply as if.

$\endgroup$
1
  • 1
    $\begingroup$ If you shadow if in your local package, you have to write cl:if throughout to access the "real" if form. $\endgroup$
    – Barmar
    Aug 21, 2023 at 17:02
5
$\begingroup$

Smalltalk just doesn’t care. true and false are just global variables. This has the advantage that it makes the interpreter simpler in some ways, but it also means that you can break a lot of things by just saying true become:false.

$\endgroup$
5
  • $\begingroup$ You can do the same in Javascript and many other languages, as stated in the question with the open example $\endgroup$
    – mousetail
    May 23, 2023 at 11:05
  • $\begingroup$ Overwriting built-in functions is rather different than overwriting true and false, though. $\endgroup$
    – Bbrk24
    May 23, 2023 at 11:20
  • 1
    $\begingroup$ Well built in functions are just global constants just like true and false are $\endgroup$
    – mousetail
    May 23, 2023 at 11:21
  • 2
    $\begingroup$ IBM PL/1 (at least early versions, the last I looked at the language was the 1970s) had keywords that were not reserved. IF IF = THEN THEN THEN = ELSE ELSE ELSE = IF; was legal and the complier could parse it correctly. $\endgroup$
    – anon
    May 24, 2023 at 4:45
  • $\begingroup$ Smalltalk doesn’t even have “if” and “else” keywords, instead its booleans just have ifTrue: and ifFalse: methods. It’s often said that the only keywords in Smalltalk are self, super, true, false, nil, and thisContext. $\endgroup$
    – Bbrk24
    May 24, 2023 at 11:05
4
$\begingroup$

Put a dot in the keyword.

This is an idea from my language that requires keywords to have fully-qualified package names.

In my language, users can define their own keywords, which are then referred to in the same way items in external packages are: the package name, dot, then keyword.

This has a side effect that I can put keywords in the standard library in subpackages in the standard library, and those keywords will not collide with variables.

For example, say I had a print keyword like Python 2. I could put it in the io subpackage, and then I could do this:

import io;

io.print "stuff";

and the user could still use print as a variable name.

You can do this even in a language without such things, though. Just make the keyword io.print instead of just print.

The key point is that the keyword doesn't have to follow the same rules as an identifier because it's special. You can put whatever characters you want in it, including characters that don't belong in identifiers.

$\endgroup$
5
  • $\begingroup$ ECMAScript too has this, but when defining an item you may need brackets there. In my ECMAScript-based language I supported an escaped identifier token for allowing defining items instead of using brackets. $\endgroup$
    – Hydroper
    Jun 1, 2023 at 21:54
  • $\begingroup$ In this example, the keyword import doesn't contain a dot. $\endgroup$
    – tobyink
    Jul 21, 2023 at 7:24
  • $\begingroup$ @tobyink keywords don't have to contain dots. They just can. $\endgroup$ Jul 21, 2023 at 21:53
  • $\begingroup$ @GavinD.Howard but if variables can be dotless and keywords can be dotless, they can still collide. $\endgroup$
    – tobyink
    Jul 24, 2023 at 16:07
  • $\begingroup$ @tobyink yes, but there's always a set of keywords that people avoid anyway. I meant my answer more in the context of "how do I add keywords when I have to be backwards compatible?" $\endgroup$ Jul 25, 2023 at 17:39
4
$\begingroup$

C# escapes keywords with @:

Keywords are predefined, reserved identifiers that have special meanings to the compiler. They can't be used as identifiers in your program unless they include @ as a prefix. For example, @if is a valid identifier, but if isn't because if is a keyword.

Documentation: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/

$\endgroup$
4
$\begingroup$

What options do we have (to tell apart keywords and programmer-specified names)?

[Well this is a rather open-ended ended question and there are a few answers already, but here are some ideas I didn't see mentioned.]

You could remove all ambiguity between the two by making them have different naming restrictions.

For example, requiring capitalization in one but not the other. E.g. if names that start with an uppercase letter were reserved for keywords, you could have something like this, where if is a regular variable and For and If are the usual compound constructs:

For if in interfaces:
    If if = mainInterface: 
        ...

Of course reserving names with uppercase initial letters would prevent the CamelCase naming style, but would still allow camelCase with a lower case initial character, and snake_case. Names starting with acronyms and the convention of writing some constants in all uppercase might be a problem though.

Having to hit shift when writing keywords might be an annoyance for some, but I think it's somewhat comparable to typing long variables with upper and lower case letters. Both can be alleviated with a smart editor, since in most cases it's likely that e.g. a programmer writing the C-like if ( a == b ) means it as the start of a conditional structure, and not a function call, even if the keyword was really If. Well at least as long as a function called if was not defined, anyway, but there are always limits to automatic correction.


As a more extreme variant, you could make every user-defined name carry some sort of a type identifier. The $ / @ / % sigils in variable names in Perl would be something like this, but you'd need to extend it to e.g. function names too.

E.g. with variables marked with a $, functions marked with a &, and the keyword unadorned, this should be technically unambiguous:

if &if($if):
    ...

Or do that the other way around, by having keywords contain some special character:

!if foo(if):
    ...

Even though some languages get away with mandatory sigils with variables, I expect this would be perceived as unnecessarily ugly by a few programmers. Also, there's more typing effort in that special characters are usually a bit harder to key in than letters regardless of case, and sometimes outright annoying in many keyboard layouts. E.g. Northern European keymaps have [ at AltGr+8, and even Shift-1 for ! requires reaching a bit farther than Shift-i for I.

$\endgroup$
2
$\begingroup$

var

In almost all cases you list, the problem arises when using a storage name (variable) colliding with some previously declared entity, variable or not.

This can easily be avoided with explicit declaring any expected local storage name before utilization. In other words, declaring/reserving local variable names:

# Works. Have priority for anything in this scope
# "var" as contextual keyword where variable declaration is possible,

var False = ''
var var = False
open(var)

# Unrestricted MRO/global mutating
# Define a variable OR change something defined... where?

open = 'hey'

Syntax option for alien, anything goes identifiers

Some languages use @ to explicitly define a user identifier (not only variables) that does not collide with keywords.

Taking inspiration from that, you could define an explicit syntax for all things "this is an identifier, it does not collide with anything" in language. The objective is not being convenient, but making it possible to interop with any external contexts.

Something like:

var csv = CsvReader.Parse( text )
var name = csv.@'Column name with \0 and spaces at end '
$\endgroup$

You must log in to answer this question.

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