Clarification: the rule is meant to prevent accessing variables that are not declared yet.Clarification 2: the rule mandates that the compiler follows calls to functions which are defined in the same scope as the call and happen before the actual definition (forward referencing)  - making the compiler much alike an interpreter.I'm thinking about a fictional programming language that behaves similar to JavaScript when declaring functions: that is, when using the function keyword to create named functions, they get hoisted to the top of the scope and can be used anywhere where their name is visible.For example, the following is legal:f();function f() { console.log(f called) };However, there is a potential problem: what if the function f uses a variable x, that is defined after the call, like here:f();var x = 1;function f() { console.log(x); }In case of JavaScript, this is also legal, because variables are hoisted as well - the program gets essentially transformed into something like this:var x;function f() { console.log(x); }f();x = 1;And therefore just prints undefined.In my fictional language, variables wouldn't get hoisted, which means that the example before would produce an error. The rule concerns how this error is generated:Functions are visible for the entire block in which they are defined,  but it is illegal to call a function that accesses a variable declared  after the call.This means that each time the compiler for this language would encounter a function call, it would have to check if the function is defined in the current scope, and if it is, check if it accesses variables in the current scope that are declared after the call.The reason for this rule is that it makes no sense to access variables before they are declared. Hoisting the variables to the top of their scopes like JavaScript does is a hack that only leaves room for error! This is why I believe it must not be done, but such situations should be detected and treated as errors (because they are, you're referencing something that doesn't chronologically exist yet).I'm looking for reasons why this is a bad rule, i.e. results in complications or inconsistencies.The reason I suspect that the rule is a bad one is that it does not exist in the Scala language. Scala solved this problem with a much more general rule:The scope of a name introduced by a declaration or definition is the  whole statement sequence containing the binding. However, there is a  restriction on forward references in blocks: In a statement sequence  s[1]...s[n] making up a block, if a simple name in s[i] refers to  an entity defined by s[j] where j >= i, then for all s[k]  between and including s[i] and s[j],s[k] cannot be a variable definition.If s[k] is a value definition, it must be lazy.UPDATE:For the time being I have adopted Scala's approach.This approach is a more general version of my rule: it will catch all illegal accesses, but also some that are not - this is a tradeoff that I'm willing to make since I think that my rule may be too complicated or even impossible to implement.Here's the rephrased/explained rule from Scala:Variables cannot be forward-referenced, but lazy values, functions defined with def (which are just syntax sugar for lambdas assigned to (lazy) values, might even remove them as Doval suggested), classes, and modules can.The restriction is that between the reference and the declaration (inclusive), there must be no variable definitions, because they might be altered by the entity being referenced before they are introduced to the program.
What negative consequences can arise from this language design rule?
programming languages;language design;scala;theory
You've already created at least one inconsistency, and a particularly damning one at that - treating functions differently from other values. A function declaration is conceptually the same as binding a variable to an anonymous function, but you're asserting that regular variables don't get hoisted while function declarations do. So...f()function f() { /* ... */ }Works, but...f()var f = function() { /* ... */ } // Pretend this is anonymous function syntaxDoesn't. (And if you don't allow passing functions around like any other value, your language is horribly crippled.) Next, what happens if a function is redefined?f()function f() { /* definition 1 */ }...function f() { /* definition 2 */ }If you treat this as an error, you're once again rejecting functions as values. There's no reason you shouldn't be able to do:var f = function() { /* def 1 */ }f = function() { /* def 2 */ }So let's assume you'll allow the function to be redefined. How do you deal with lexical scoping?function f() { /* definition 1 */ }function bar() {    f()    function f() { /* definition 2 */ }If you hoist definition 2 to the top of bar's scope, you end up with this:function f() { /* definition 1 */ }function bar() {    function f() { /* definition 2 */ }    f()Which means you can never call the outer definition from a nested scope unless you give the inner function a different name. Moreover, in any other language, the meaning of the above program is independent of the choice of name for definition 2. The hoisting rule changes the meaning depending on whether there's a name collision or not. On the other hand if you don't hoist definition 2 to the top of bar, you've broken your own rule.