分享

lua

 quasiceo 2014-01-19
I would like to address the arguments given against having "local by default" be possible in lua, instead of the current "global by default" (upvalue by default) status for implicit variable declarations. I realize these aren't all arguments at all, but several folks on the IRC channel suggested that LocalByDefault was the go-to source for learning why "local by default" is a bad idea and "global by default" is fine.

First I'll outline my understanding of how "local by default" would work. It could be on a module-to-module basis, with some sort of pragma in the file to indicate that. Then I'll explain some reasons global by default is terrible. Finally I'll address the actual arguments presented on that page, if they are even arguments for "global by default" at all.

Upon assignment the first time to a variable, it creates a local variable by that name just as if "local" were specified. A keyword either "global", "nonlocal" or "upvalue" would indicate exceptions to that, when a variable is in fact an upvalue. The keyword could take a variable, a variable assignment, or several of those things as is convenient. What follows is a hopefully complete example of this syntax.

in a file, call it module.lua:

default local

upvalue foo, bar
print("All variables on the module level are local, except foo and bar")

a = "a.module"
b = "b.module"
c = "c.module"

function foo(a)
    c = "c.foo"
    print("c is local",c)
    print("a is local",a)
    print("b is upvalue",b)
end

function bar()
    c = "c.bar"
    print("c is local",c)
    print("a and b are upvalues",a,b)
    foo("arg.foo")
    assert(c != 23)
    assert(c != 3)
    assert(c == 4)
end

function baz(a,b,c)
    print("Everything is local in here",a,b,c)
end

baz(1,2,3)

function blech()
    print("Everything is upvalue in here",a,b,c)
end

blech()

function outer()
    upvalue b
    function inner(a)
        upvalue b,c
        print("b is upvalue, which is upvalue to module level",b)
        print("c is upvalue, to the outer function",c)
        print("a is local",a)
    end
    print("Now a will be arg.inner, b will be b.module, and c is nil")
    inner("arg.inner")
    a = "a.outer"
    c = "c.outer"
    print("Now a will be arg.inner, b will be b.module and c will be c.outer:")
    inner("arg.inner")

    b = "b.outer"
end

outer()
print("Whoops, b was an upvalue, so it modified the module's b to b.outer",b)

function outer()
    a = "a.ohno"
    function inner()
        upvalue a
        print("a is a.module wait no it's a.ohno",a)
    end
end
outer()

print("note outer, blech, baz, a, b and c are all module local variables. You can only call foo and bar when requiring this.")

(Note: this isn't valid Lua. It's my concept of what Lua ought to look like.)

An obvious limitation is that for an innerly declared function to access a module level variable by the same name as the outer function's local variable, the outer function will have to declare it an upvalue too. This could be mitigated with a numeric argument to upvalue for how many frames to go up. There could additionally be "global" and perhaps "module" keywords for absolute scope regardless of depth of nesting. Currently lua does nothing to mitigate this, but the instance where you run into this problem is sufficiently rare that people just work around it.

The reason that global by default is a bad idea is one of limiting the destructive scope of a human error. When you forget to specify local, and "local by default" is in effect, then your function continues to work perfectly. When you forget to specify "global" or "upvalue" or whatnot, then your function does not modify any of the program's global state, producing no side effects and making nothing else work differently. When "global by default" is in effect and you forget to specify local, then your function both works and does not work depending on what happened to that global variable during the function's progress. Additionally when your function changes the global variable mistaking it to be local, other functions where the same mistake was made will elicit the same behavior.

The reason that global by default is a bad idea is because globals are a bad idea. We do not need a language that makes their use easier than locals.

Point blank, if implicit declarations must exist at all, and not be a syntax error, then when we forget to specify the scope of our variable, global by default can cause unpredictable program state, side effects and can break formerly working completely unrelated code without altering that code. That's terrible! If global by default were a requirement then we might put up with this shiznit, but an alternative exists! Global by default is terrible and we should use local by default instead.

There's also the "copy-and-paste" issue, though that comes up more rarely. When you copy a code fragment from one place to another, it's possible to forget to select the "local" declarations at the top. Additionally you might have forgotten local declarations at all, so when you copy the code into a different function, then both functions will use the same global variable unintentionally It's practically impossible not to have both functions not blow up mysteriously for no reason when you do this.

Now, to address the arguments in favor of "global by default" and why they are miscast.

  • [4] "Local by default is wrong. Maybe global by default is also wrong, [but] the solution is not local by default." (Roberto)

    • This is a red herring. What we're talking about is replacing global by default with local by default, to solve different problems than Roberto is thinking of. He says "the solution" but what problem is it the solution to? Implicit variables are tricky to manage, period. He wants nothing by default, to make people write better code. Others argue the convenience of implicit declaration. None of this has anything to do with, if you must have implicit declaration, whether local by default is better than global by default or not. Saying "Local by default" is wrong is sort of misleading, since he makes no differentiation between local by default and global by default

  • [5] "I don't really understand why anyone would want variables to be *anything* by default, as in automatic declarations. All it does is save you a few keystrokes every now and then, while opening up for endless fun looking for stupid, hard to find typo and name mixup bugs. (Assignment instead of declaration and vice versa.)" (David Olofson)

    • More of the same red herring. We're not arguing whether implicit variables are a good idea at all, only what scope those implicit variables should be in.

  • [6] "The problem is that without [explicit] local declarations, you cannot say where the variable is local to." (RiciLake)

    • That assumes your language is "global by default" so it's not really an argument against "local by default" languages. If implicit declarations are local, then the variable is local to the innermost scope every time. You specify when it must be beyond that.

  • [7] "'local by default' does not mix well with lexical scoping....Proper lexical scoping really requires that you have a choice between mutating a binding (assignment) and creating a new binding (declaration). Alas, the 'local by default' rule mixes them and makes an assignment do both." (MikePall)

    • The "global by default" rule mixes them too. This is an argument against a problem we're not trying to solve. The question is not whether we should have implicit declarations at all. The question is that given we have them, what should we do with them? Having a problem with "an assignment [doing] both" is complaining about the existence of implicit scoping at all. He has not made an argument for or against global by default or local by default.

  • [8] "It seems to me that the main argument for local-by-default, is to prevent accidents with the global environment, and secondly to make declaring locals optional, thus having to type less. Personally, I think making declarations optional is a bad idea, both for locals and for globals. To force explicit declarations for globals, as well as to prevent accidents with the global environment, I use require 'strict'. Maybe it's possible to do something equivalent (or better) at compile time. But my point is, perhaps one should ask oneself, whether one really wants/needs local-by-default, or just explicit globals declarations." (Mark Meijer)

    • This is another guy who isn't arguing that global by default is better than local by default. He has made no case either way. He could prefer local by default for all we know. He's just arguing against implicit declaration upon assignment, like most everyone else. The page says he's arguing in favor of "global by default" but in fact he's arguing against anything by default.

  • [Python PEP 3104] -- Python's fix to "local by default", which is identical (apart from the choice of keyword!) to the proposal by ReubenThomas (see below).
    • Yeah that works pretty good actually. This isn't an argument at all. The difference between "nonlocal" and "global" is that nonlocal is an upvalue, and global is at the module level. (Python has ZERO variables in the global scope. You can't import a module and have variables magically appear in your current module. This is a good thing.

  • [1] "[The current local variable scoping rule] is the single biggest design flaw in Ruby." (Yukihiro Matsumoto, Ruby designer) (comments: [9][10][7])

    • The biggest design flaw in ruby is that it has a global scope at all. You'll see modules written that are not modules at all and do not make a coherent program, on the assumption that all their stuff will be required beforehand. That's really a stylistic issue, since lua is fully capable of terrible habits like that. Only python has it so you have to assign to the module to change any variables the module sees. But hey opinions opinions. How about ruby's slow!

  • [2] -- Discussion by the various scoping rules and why the language Converge chose "local by default" with a "nonlocal" (like PEP 3104) keyword. (Laurence Tratt, Converge Designer)

    • This is an argument for local by default.

  • [3] "I dislike the implicit declaration of lexicals because it tends to defeat the primary use of them, namely, catching typos...Declarations should look like declarations...I believe that declarations with my are properly Huffman encoded." (Larry Wall, Perl Designer)

    • Another "any implicit declaration is bad" argument. It doesn't say whether or not implicit declarations should be global or local.

I personally used to think implicit declarations were bad, and that explicit declarations significantly reduced your errors and prevented bugs. Then I realized it did neither of these things, and thus got sick and tired of typing boilerplate just to satisfy some ivory tower eggheads whose claims have been soundly disproven by the success of implicit variables and their ease of use without errors cropping up. They claim it's more proper or better, but it's just one syntax versus another. Nothing says combining assignment and declaration is a bad thing. They can't assume something unproven (or even disproven), and argue for mandatory explicit declarations based on that assumption!

I might be a bit biased.

But if implicit declarations must exist, it is extremely important that they be local by default, to prevent very real and hard to track down programming errors. So, even if you don't singlehandedly rewrite all Lua code to be local by default, please stop saying that global by default is better.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多