Constness

From Helpful
Jump to navigation Jump to search
Some fragmented programming-related notes, not meant as introduction or tutorial

Data: Numbers in computers ·· Computer dates and times ·· Data structures

Wider abstractions: Programming language typology and glossary · Generics and templating ·· Some abstractions around programming · · Computational complexity theory notes · Synchronous, asynchronous · First-class citizen

Syntaxy abstractions: Constness · Memory aliasing · Binding, assignment, and such · Closures · Context manager · Garbage collection

Sharing stuff: Communicated state and calls · Locking, data versioning, concurrency, and larger-scale computing notes

Language specific: Python notes ·· C and C++ notes · Compiling and linking ·· Lua notes

Teams and products: Programming in teams, working on larger systems, keeping code healthy · Benchmarking, performance testing, load testing, stress testing, etc. · Maintainability

More applied notes: Optimized number crunching · File polling, event notification · Webdev · GUI toolkit notes

Mechanics of duct taping software together: Automation, remote management, configuration management · Build tool notes · Installers


This article/section is a stub — some half-sorted notes, not necessarily checked, not necessarily correct. Feel free to ignore, or tell me about it.


In programming languages that have it, const is mostly about you telling the compiler about what you won't do with a variable, in the scope you declare this in in.

In languages that are statically typed, you say you will not change the value.

In languages that are dynamically typed, you say you will not assign another object reference to this name.

...and that difference can be subtle (and there are some futher subtleties depending on how your language, e.g. with how it thinks about binding versus assignment and the scopes of such).


The practical reality of a language might have cases you can talk about more specifically, you might e.g. have

  • a const global that promises it won't be altered at all,
  • a const function parameter to promise that the function won't alter it (e.g. C, C++, C#)
  • a const variable within function scope (e.g. C, JS), a const thing in a narrow block scope (e.g. JS), etc.
  • no constness beyond convention (e.g. Python) and perhaps some syntax tricks to make it effectively read-only

This is primarily a tool to allows some basic checks that code does not alter these (not-so-)variables, in the form of the compiler throwing errors when you try.


And yet, const generally does less than you think

People have a few different ideas of what const might do:

  • make the compiler complain about attempts to never alter data that is declared const
  • let the compiler optimize

Both turn out to be limited.


Say, constness means you cannot accidentally reassign a variable in the same scope, because it is very clear to the compiler when you do that. So the const variable will, in its lifetime, always be bound to the thing you assigned to it when you defined it.


At the same time, in many languages it does nothing to prevent you from altering the contents of the thing that it points to.


In more precise terms, it can be a constant reference to a value or object that might itself be mutable. Even if not here, then possibly elsewhere.

The problem is often that constness is part of the type in a scope, not a property of the thing it points to (regardless of whether the language is statically or dynamically typed, and regardless of whether you can tell the compiler separately that the reference and the value are const, as in C++)


Consider const in javascript.

const a = {}
a   = 2;     // will be a SyntaxError
a.b = 3;     // is perfectly fine


C and C++ are a little more interesting, see C_and_C++_notes_/_Syntax_notes#const, in part because both the pointer and the value can be const or not - and that can still be defeated.


What about optimizations?


The reason constness doesn't lead to many compiler optimizations is similar: you don't know how far the constness promise goes, or who can violate it, so it leads to very few things you can actually guarantee.

Pretty much the only case are things that are assigned and declared const at the same time - like global constants. These can actually be moved to the executable image. And yet for primitives this doesn't give you much over a #define.


(Similarly, the lack of guarantee of immutability means it also doesn't help around parallelism)

This is probably more true in C/C++ where pointers make things complex; in JS there are probably a few more cases where it does help.

And what would it improve? Say, in C++, if you want to avoid some copies, then pass-by-reference basically already does that.


Consider, for example, a library that wants to be given const strings so that it might be a little more efficient if it promises the compiler it won't via const. They were probably non-const to begin with, but treated as const by that library, maybe just to inform you that it's safe to pass by reference without issues like those from side effects. Sounds good, right?

Now consider another library that doesn't, that alters the strings in-place for optimization reasons they might call zero-copy or whatnot. This breaks the first library's promise to the compiler (and your promise to the other library).

Usually that won't happen. ...for reasons unrelated to constness, and that constness has no say over.+


It may still be useful to keep in that library, just to signal to you what it is and isn't doing, and perhaps how you should treat it.