In Python, a mistake that I’ve made is having the wrong indentation for the last line of a loop, which changes the semantics of the program. The statement is either done in a loop when it should have been done once or vice-versa, and it is difficult to tell what was intended.
Most of my career, I programmed primarily in C or C++. When I discovered Rust, I found that it seemed to have almost everything I’d wished for in C++. Later, when I discovered Ada and as I learned more about it, I found that it additionally had things I didn’t know I wanted or needed. The design of the syntax to help to prevent common errors is one of the impressive things about Ada.
But being able to mark the end of a scope with information about which scope-beginning tag it was associated with was one of the things I’d wished for in C and C++ that Rust didn’t deliver. Ada comes adequately close to delivering what I’d wished for. I used to put comments after my closing braces like }// if to indicate which opening brace that closing brace was associated with. Sometimes I copied the whole line or a major part of the whole line starting the scope into the comment like } // if (0 == x) to distinguish between multiple statements, maybe nested.
One of the things that can happen is a mistake with cutting and pasting, and at some point, I decided that I’d like the compiler to be able to tell me when I’d made such a mistake. Of course, you might say that I shouldn’t be cutting and pasting, but eventually it becomes hard to avoid. And, of course, I know to do it with care, but I am human.
Here were some of the things I tried in C++ (According to my recollection, I didn’t use these professionally; I just experimented with them at home):
if (const bool test_in_if_stmt_scope = true)
do {
... ;
} while (!test_in_if_stmt_scope); //do once
The point of that code was that the closing brace couldn’t be separated from the declaration of the variable in the if statement without resulting in an compile-time error.
I also found this code:
/*! class closing_brace_marker_type_
* \brief type for marking a closing brace as distinct from other closing braces
*
* Use this class by creating an instance with a unique name at the beginning of a scope
* Use the instance as the last program statement within the scope, right next
* to the closing brace.
*
* If a closing brace intended for another scope is accidentally (e.g., by cut and paste)
* placed in this scope, the compiler will complain the instance was not declared in scope.
*
* example:
* while (!done) { closing_brace_marker_t closing_brace_marker_for_while_not_done_scope;
* ... //program statements
* closing_brace_marker_for_while_not_done_scope = 0}
*/
typedef class closing_brace_marker_type_ {
public:
const closing_brace_marker_type_ &operator=(int a) { a=a; //a is a dummy value
return *this; }
template <typename T>
static const T &identity(const T& val) {return val;}
} closing_brace_marker_t;
The code is about a decade old, and the comment doesn’t explain what the identity function is for, but I found an example where I used it:
int num_shared_coreferences = [](int article_num, int claim_s_id, int evidence_s_id) { closing_brace_marker_t closing_brace_marker_for_num_shared_coreferences_lambda;
...
return closing_brace_marker_for_num_shared_coreferences_lambda.identity(temp); }(claim_article_num, claim_sentence_id, evidence_sentence_id); //end of lamda definition
Apparently, it was to be used when the last statement before a closing brace was a return statement. In this example, the braces are part of the scope for a particularly long lambda function definition, and the function is called immediately. That was a technique I used sometimes to ensure that my variables were assigned an appropriate value before they were used, and C++ doesn’t have another way to make nested or anonymous functions. A good alternative would have been a global function, but since C++ lambdas are not closures by default, I could later just cut and paste the contents of the lambda to a new global function assured that it was not dependent on any variables in the outer scope (because the brackets at the beginning are empty).
If you know C++, you might notice that I could have just used an int instead of a dedicated type and declared it at the beginning of the scope and assigned it at the end. As long as I was using such long names for the variables, there probably wouldn’t have been any naming conflicts. I suppose I wanted to be as explicit as possible about its purpose.
I also found comments like this in my old C++ code:
{ //BEGIN SCOPE FOR LOADING CACHED SIMILARITY SCORES
...
} //END SCOPE FOR LOADING CACHED SIMILARITY SCORES
To OneWingedShark’s point, the actual syntax used to start or end the scope is less important than the fact that it is made difficult to mix up scopes and that the scope one is looking for is easy to find.
Rust and/or C++ could extend their braces syntax to add labels at the beginning and after the end, but as it is, the braces syntax was not even providing the functionality that I wanted before I discovered Ada.