8
$\begingroup$

As of C17 there are no built-in constants. To use true false NULL or similar one must #include various header files. However this will change in C23 and true false and bool will be keywords.

In Java there is Math.PI instead of having PI or pi as a keyword.

Is there any reason to do that as opposed to just making them keywords that behave as literals? What are the advantages and disadvantages of having constants built-in versus having them imported from library/header files?

$\endgroup$
4
  • $\begingroup$ Note that in C23 the _Bool type and stdbool.h have been replaced by a bool type with values true and false (see cppreference) $\endgroup$
    – RubenVerg
    Commented Jul 4, 2023 at 16:49
  • $\begingroup$ @RubenVerg That is correct. $\endgroup$
    – CPlus
    Commented Jul 4, 2023 at 16:50
  • 2
    $\begingroup$ This is two separate things - firstly whether they should be keywords, and secondly whether they should be available in the global scope. (If they're keywords then the answer to the second part seems to be obviously yes.) Python exhibits all three options ─ None, True and False are keywords rather than valid identifiers, __builtins__ is a predefined dictionary in the global scope, and math.pi is a constant in a separate module. $\endgroup$
    – kaya3
    Commented Jul 4, 2023 at 16:51
  • $\begingroup$ @user16217248-OnStrike missed the 17 in C17, sorry, guess this is interesting anyways $\endgroup$
    – RubenVerg
    Commented Jul 4, 2023 at 16:57

5 Answers 5

11
$\begingroup$

Some constants (true, false, null, maybe some others in specific languages) are so important that they need to be in scope everywhere. It would cause major problems if the user were allowed to shadow them, and it would be very inconvenient if they were only accessible through qualified names like Boolean.TRUE. By making these keywords, the user cannot declare variables with the same names, and then shadowing is impossible.

In languages where all variables are mutable (i.e. "constants" are just variables you choose not to reassign), it would even be a "WTF" for fundamental constants to be treated the same way as other variables. In very old versions of Python it's legal to redefine True, False and None, and in Javascript (in "sloppy" mode) it's legal to redefine undefined. Many people found this ridiculous, and it was fixed in later versions of Python and strict-mode Javascript.

On the other hand, keywords are part of the language grammar, and if you did this for every constant then the grammar would change every time you add a new constant to the standard library, and this would invalidate existing code which uses those names for variables. (Keyword constants can't be contextual or "soft" keywords like match in Python, because they appear in the same contexts as variable names anyway.)

Additionally, constants like PI or MAX_INT aren't used practically everywhere, and don't necessarily need to always be in scope or be protected against shadowing. So in these cases I think it makes more sense to treat them like regular constants or variables, with the same semantics as those that users are able to define.

$\endgroup$
5
  • 2
    $\begingroup$ Obligatory mention of false become:true in some dialects of Smalltalk. $\endgroup$
    – Bbrk24
    Commented Jul 4, 2023 at 17:45
  • 3
    $\begingroup$ You still can redefine Python3's constants: CPython boxes them all and caches a few of them on startup, and id returns the shared objects' locations. ctypes.c_int8.from_address(id(4) + 24).value = 5 rewrites 4 to 5. $\endgroup$
    – Longinus
    Commented Jul 4, 2023 at 18:42
  • 5
    $\begingroup$ @Longinus That's true in CPython, but it is not part of the language design. It's also hilarious, because it doesn't just rebind the "name" 4, it also changes the result of 2 + 2 (due to small integers being cached). $\endgroup$
    – kaya3
    Commented Jul 4, 2023 at 19:03
  • 1
    $\begingroup$ @Bbrk24 When I was managing Lisp Machines, one of my users once redefined NIL or T. A system crash soon occurred. Symbolics made them constants in the next update. $\endgroup$
    – Barmar
    Commented Jul 6, 2023 at 22:21
  • 1
    $\begingroup$ It's worth noting undefined was never intended to be mutable, but Internet Explorer did what it does best and messed up the implementation, and everyone else kept the behavior for backwards compatibility. $\endgroup$ Commented Jul 7, 2023 at 16:58
5
$\begingroup$

Because they can be

There is no real need to give these constants special treatment in the compiler. Normal values would function exactly the same in most cases. Not giving them special treatment but simply defining them in the standard library is easier.

Not all of them are used very much

While true, false, and null are common enough to have in global scope, something like PI is only really used in trigonometry. It's not that much effort to type Math. in front of it when you need it.

PI is used in many math formulas with a different meaning than 3.14... If Pi was always in the global scope you couldn't name any variables PI without leaving you unable to access the circle constant.

$\endgroup$
5
  • $\begingroup$ Not to mention politics - Indiana once passed a bill that set π to 3.2. $\endgroup$
    – Barmar
    Commented Jul 6, 2023 at 22:22
  • $\begingroup$ @Barmar they didnt actually pass the bill $\endgroup$
    – Seggan
    Commented Jul 7, 2023 at 0:33
  • $\begingroup$ @Seggan-OnStrike Their House of Representative passed it, but it died in the Senate. $\endgroup$
    – Barmar
    Commented Jul 7, 2023 at 0:36
  • $\begingroup$ @Barmar my point still stands $\endgroup$
    – Seggan
    Commented Jul 7, 2023 at 0:36
  • $\begingroup$ @Seggan-OnStrike OK, they tried to pass a bill that.... $\endgroup$
    – Barmar
    Commented Jul 7, 2023 at 0:37
3
$\begingroup$

Alternative solution: static properties + inferred base type

Swift has things like this:

extension Double {
  static var pi: Double { 3.14... }
}

Swift also has inferred base types for static variables:

let x: Double = .pi // inferred as Double.pi

These extensions can live in other modules, so they don't have to be built-in, but the inferred base types lower the barrier to use, in a sense.

$\endgroup$
3
$\begingroup$

You might not know the type and precision required

Consider Pi as a constant in a C like language

Should it be a float or a double or a 128 bit precisions float or perhaps even an arbitrary precision bigint?

How many digits do you need?

Though in fact pi has arrived in C++20. That it took so long to get there should hint there are some issues.

You may be trying to guess your users requirements

If you provide pi as float but actually most of your users wants a double you have polluted your standard namespace and everyone is rolling their own double_pi.

what do you mean by builtin?

Builtin constants are unnecessary bloat if you don't use them. Maybe I don't have any use for pi in my application.

Your constant is not as constant as you think

Say you define PATH_MAX as a builtin constant for the maximum length of a path. Does that vary by operating system or file system? Is it something your application should actually get from calling a function at runtime? See for example https://eklitzke.org/path-max-is-tricky

Some constants are safe and good

It seems pretty sane for true and false to be builtin.

Beware automatic type conversion

But to give them values of 1 and 0 might be a unwise if your language supports arbitrary conversions to and from different kinds of number.

Booleans may be better off being left as a distinct type.

$\endgroup$
2
$\begingroup$

C Has An Infinite Number of Builtin Constants

C17§6.4.4. and §6.4.5. define four different kinds of builtin constants: integers, floating-point numbers, character constants and string literals.
Each allow for an arbitrary number of either digits or characters (including in character constants).

C does not need to offer any of them as part of its feature set. Integrals can be represented fully using Peano's Numbers:

type 0
type ℤ⁺ : (ℤ⁺ | 0) -> ℤ⁺
type ℤ⁻ : (ℤ⁻ | 0) -> ℤ⁻
ℤ = 0 | ℤ⁺ | ℤ⁻

5 = ℤ⁺ (ℤ⁺ (ℤ⁺ (ℤ⁺ (ℤ⁺ 0))))
-4 = ℤ⁻ (ℤ⁻ (ℤ⁻ (ℤ⁻ 0)))

Look ma! No builtin numbers!

42 is as much a keyword as are true, false and null.
Many languages, including C-like ones, don't even have builtin negative literal constants, and instead have a unary minus operator that you apply to numbers.

$\endgroup$
2
  • 2
    $\begingroup$ -42 isn’t a literal, it’s unary minus applied to 42. This means that INT_MIN can’t be -2147483648, it has to be -2147483647 - 1. $\endgroup$
    – Bbrk24
    Commented Jul 4, 2023 at 19:30
  • $\begingroup$ @Bbrk24 Good point! Many languages only have builtin positive constant literals! $\endgroup$
    – Longinus
    Commented Jul 4, 2023 at 21:05

You must log in to answer this question.

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