Warning, wall of text incoming!
“In computer programming, scope is an enclosing context where values and expressions are associated. The type of scope determines what kind of entities it can contain and how it affects them. Typically, scope is used to define the visibility and reach of information hiding.”
The scope of an entity’s name is the set of all declaration spaces within which it’s possible to refer to that name without qualification. In general, the scope of an entity’s name is its entire declaration context; however, an entity’s declaration may contain nested declarations of entities with the same name. In that case, the nested entity shadows, or hides, the outer entity, and access to the shadowed entity is only possible through qualification.
So much for theory (the quoted text was from Wikipedia). Now, what does that mean for CoolBasic…
In VB.NET and C# there are mainly two kinds of scopes: We have a root/namespace/class/structure context, and then we have a method/block context. The main difference between the two is how data can be referenced in Object Oriented Programming. Class instance variables (objects) store their values in heap because they are reference types and their life span is not determined beforehand by the compiler. Procedure variables, however, store their values in stack. It’s a static space in memory where procedure variables temportarily reside, but from which the space is automatically freed when the life span of all those variables ends, i.e the procedure exits. Because of the stack, recursive calls are possible.
It’s important to realize that the program structure is highly hierarchical in Object Oriented Programming. It’s all about references to other objects and their parent-child relationship. Gone are the days of procedural programming and direct function calls where the only way to share data between two functions, was to introduce a global variable to hold that data. Now, global variables are quite bad a concept and frowned upon by most programmers today. That’s because in general, it’s often difficult to keep track of the value of a global variable plus all entities that can modify it. In the end, there’s more cons than pros when we’re dealing with large and complex projects.
I will not talk about global variables (or the lack thereof) just yet. Nor will I define concepts of Inheritance, Shadowing or Overriding because in all probability I’m going to do so at some point in future anyway. For quick reference, “inheritance” means reference-based access to a parent class from a derived class. “Shadowing” means identifier matching to the nearest level upwards within the program tree. “Overriding” means priority of methods, operators and properties through inheritance for identifiers with matching signature. “Implementing” means copying functinalities from an interface-template.
In VB.NET and C# there are some restrictions what you can and can’t declare within each type of entity. The basic concept is that all code is written inside a class. The only exception would be the root context which itself behaves like a class. However, root level should have the main object created as soon as possible so the program can be redirected to true class-based execution. Classes can have methods (functions), operators (kind-of functions, but executed as operator instead of value), properties (a public attribute with functional purpose) and even inner classes. In addition, there can be structures (similar to classes), enums, constants, and attributes (variables). I’ll describe some restrictions that were mentioned in the MSDN documentation, but were never explained or justified why you can’t do these things. I had to figure these out myself, and there might still be some caps behind my logic. However, trying to understand the problem by oneself tends to be quite an enriching and valuable experience.
A function cannot be declared within the Block or Method context
A Block context does not have an identifying name that you could use to access it by using the dot notation. This itself is logically wrong. In addition, the member access operator (dot) only works with references, and neither block nor function is a reference. This means that functions don’t get unique memory addresses that are visible to the programmer. Even if you had a pointer to the function, it’d be still pointing to the code entry point and not to the actual member variables. Function members can never be Public. They can’t be Protected either, because functions cannot inherit from other functions. So function members are Private which means you couldn’t access them from the outside anyways. Function variables and their memory addresses will be determined during runtime because they reside in Stack. This offset can’t be calculated at compiling time. References, in the other hand, point directly to member variable addresses so the reference is always direct and valid.
This, however, doesn’t explain why the compiler couldn’t just enforce Private functions within the Block/Method context (all identifiers are local in these context levels). Firstly, there’s the logical issue: Why on earth would anyone want to defined private functions inside other functions. It’s like restricting yourself from data you’re eligible to use anyways – for no gain. Secondly, there’s the technical issue. While I *could* allow this kind of declaration in CoolBasic (even though it’s forbidden in .NET-languages), I think that it would be better for the compiler’s sake to prevent from mixing two separate scope definitors. Private/Public/Protected are intended to define reference context accessibility, the inheritance thing. Whereas keyword Dim is meant to define identifiers within nested local scopes. There’s no equivalece to Public in Method/Block context (if there was, you’d be able to call conditional code from outside, which could potentially break the program flow integrity), as explained above.
So let’s just assume that we have a function inside a local scope (another function or a block) with no separate access modifier in the declaration. The declaration is now legal as long as there are no inheritance-related modifiers or flags. We call this inner function only from inside of the hosting scope. What’s wrong with this? Well… nothing really, besides the odd visibility and reach. Can we implement this? Probably. Should we allow this? I don’t know. It just happens that when you enter a method scope you can, in fact, declare an identically named inner function. In this case, which function does a recursive call direct to? In normal rules of shadowing, it would be the same scope of the calling code unless qualified with Me or MyBase. The problem is, these two variables always refer to a class so you can’t point to the inner function. Either you use Me.myFunc()
for the outer function or just myFunc()
for the inner function. Also, it matters whether you make the recursive call from inside the inner function or from outside of it. Add different function signatures and you have a nice soup of failure.
As what comes to declaring classes within functions, the same rules kind of apply. You can’t use function name as parth of the dot notation, to point to a class or nested function. Even though the issue would not be reference-based at runtime (procedures get their fixed location within ASM at compiling time). It’s about the syntax and whether or not it’s allowed to do inconsistent/logically wrong things. Function references within the source code should always contain a set of brackets – which itself satisfies the member access operator (dot) because such a token will be identified as value evaluation.
A structure or enum cannot be declared within the Block or Method context
First of all, being unable to declare structures within procedures, but still being able to do so outside (which the function could use), made no sence to be. There shouldn’t be any restrictions becasue you can declare variables normally. Only so that the new structure/enum would be visible locally within the method scope. Some rules that were explained above still apply, but I think I have come up with a better explanation. Picture this: You have a structure called “myStruct” within a class context. Then you have a method that takes a typed variable (myStruct) in as a parameter. Because the scope has now changed, you can declare another structure that has the same name within the method. When the source code gets parsed for the first time, all identifier definitions are scanned. While it wouldn’t normally matter where you have the structures defined and still being able to use them from anywhere within its scope, it just happens that variables are identifiers, too. So they get scanned in the same way. You *could* do multiple passes for the source code to scan in layers, but in OOP this can be a bit problematic and slow. So in this scenario, you can define a variable as “myStruct” before and after the inner Structure is declared. Which means we have two different types for those variables even though they were typed using the same name. Confusing, eh?
Conclusion
When you think about it, the answer to all these problems could be the simplest one: They don’t want you to write structural definitions (such as structures, enums, functions or classes) to a code block which has executable code part (and thus no no declaration part that is separate). The basic priciple is that everything belong to their little boxes, and only access modifiers, inheritance and shadowing rule out information hiding and reach because they work in both ways in the program hierarchy. I think there’s enough reason already to conclude this think-tank into following the rules the wise guys at Microsoft have agreed upon, and not to allow nested functions in CoolBasic V3.