First of all, sorry to bother you again with this issue, but I guess it
didn't have the appropriate discussion. If you're not interested, please
don't read further...



I wrote:
> I expect Perl 6 will have some way to define its variables as being
> lexical-scoped in the sub they are used as default, either by the language
> default, or by a pragma, as `use scope "subs"; ', as it's proposed in RFC
> 64.

John Porter wrote:
> Well, since the former isn't going to happen, the latter isn't going to
> be a problem.

and Michael G Schwern wrote:
> I believe this issue was discussed to death on the mailing list Way
> Back When and IIRC the result was "its too much trouble to eliminate
> two characters".  There's heaps and heaps of caveats, arbitrary
> decisions and conflicts to be resolved about how the auto-lexical
> scoping should work.  All this so you don't have to type "use strict"
> and the occasional "my".
[snip]
> Anyhow, I could be completely wrong about how the discussion turned
> out.  Check the archives.

Well, I checked the archives, and I found that the discussion begun in
http:[EMAIL PROTECTED]/msg01441.html and the
last post about it was in
http:[EMAIL PROTECTED]/msg01567.html, by Bart
Lateur. And that post was IMO beginning to go into a different direction,
but I saw no comments about it. Did I miss something in the archives? I
looked for it in perl6-language-strict as well but found nothing.



Well, first let me say why I think a way (pragma) to do lexical-scope by
default (for one file/block/scope) would be good. Most (modern) languages do
it and most programmers are used to it. And `my' really doesn't DWIM. For
example,

    my $a, $b, $c;            # only $a is lexically scoped
    my ($a) = <FHANDLE>;      # after deducing (by the above)
                              # that I should include parenthesis
                              # around every `my', this example
                              # comes to read the whole file
                              # when I wanted only the first line.

    if ($x) {
        my $y = 1;
    } else {
        my $y = 2;
    }
    return $y;                # wrong, since my makes $y above
                              # only inside the if/else blocks.

    # I have to do:
    my $y;
    if ($x) {
        $y = 1;
    } else {
        $y = 2;
    }
    return $y;


    # I know you'll say I could use
        my $y = $x ? 1 : 2;
        return $y;
    # But suppose I wanted to write more code
    # inside the if/else blocks and supply a
    # return value.



These are the problems I see with `my', not to mention the problems that
arise by NOT having `my' (these are quite well described in RFC 64).



My proposal is: As most code written SHOULD have (almost) all its variables
lexically scoped, in presence of a pragma (like `use scope'), all variables
would be implicitly defined as lexically scoped to the named sub (not
anonymous blocks, only subs) they're in. To access variables outside of this
scope, keywords would be used to statically define that a variable actually
refers to a global variable or a variable of another scope.

I propose the introduction of two new keywords (just like `my' and `our')
for specifying a different scope: `global' and `outer'. `global' would be
used to say that a specific variable or a list of them would refer to the
global variables with those names in the current package. `outer' would be
used to say that a specific variable or a list of them would refer to the
same variables in the `parent' level (that would be used for closures).
Variables accessed with their explicit full packagenames would be the global
variables. Variables in the top-level in the file would be file-scoped. subs
that want to access these file-scoped variables do it with `outer'. `my'
would still be allowed, for lexically scoped variables inside some blocks
inside a sub that should have variables of its own.



When I originally wrote my `outer' proposal (it was named other thing then),
John Porter wrote:
>
> Ugh - upvar?  No thanks.
>

Actually, what I'm proposing is quite very different than `upvar'. `upvar'
is a dynamic thing, it accesses a variable in the scope of the caller.
`outer' is a lexical thing, it tells the compiler that that variable name is
accessing the same variable that the definer was accessing.

For example:

    # In a very Perlish Tcl...
    my $x = 1;
    sub foo {
        upvar $x;
        print $x;
    }
    sub bar {
        my $x = 2;
        foo();
    }
    bar();
    # would print 2


    my $x = 1;
    sub foo {
        outer $x;
        print $x;
    }
    sub bar {
        my $x = 2;
        foo();
    }
    bar();
    # would print 1


As `outer' accesses the variable of the `definer' and when it was `defined',
it accesses the file-scoped `$x', instead of the caller's (bar's) `$x', that
is what would happen with Tcl's `upvar'.



----------



Examples of usage:


    use scope;
    sub foo {
        $bar = shift;
        if ($bar) {
            $baz = 0;
        } else {
            $baz = 1;
        }
        return $baz;
    }

would compile as:

    sub foo {
        my $bar = shift;
        my $baz;
        if ($bar) {
            $baz = 0;
        } else {
            $baz = 1;
        }
        return $baz;
    }

----------

    use scope;
    $x = 0;
    sub counter {
        outer $x;
        return $x++;
    }

    print counter();    # prints 0
    print counter();    # prints 1

would compile as:

    my $x = 0;
    sub counter {
        return $x++;
    }

----------

    use scope;
    sub make_counter {
        $x = 0;
        return sub {
            outer $x;
            return $x++;
        };
    }

    $counter = make_counter();
    print $counter->();    # prints 0
    print $counter->();    # prints 1

would compile as:

    sub make_counter {
        my $x = 0;
        return sub {
            return $x++;
        };
    }

----------

    use scope;
    package main;
    global $x;
    $x = 1;
    sub foo {
        $main::y = 2;
    }

would compile as:

    $x = 1;
    sub foo {
        $y = 2;
    }

----------

    $x = 1;
    {   use scope;
        sub foo {
            $x = 0;
            print $x;
        }
    }
    foo();       # prints 0
    print $x;    # prints 1

would compile as:

    $x = 1;
    sub foo {
        my $x = 0;
        print $x;
    }

----------

Note that `outer $x, $y, $z' is the same as `outer($x, $y, $z)' in contrast
to `outer($x), $y, $z'. The same is valid to `global'. `outer $x = ...' is a
syntax error, and so is `outer($x, $y) = ...'. The statements should be each
on a separate line.



I predict that you're going to say that this isn't saving typing, and in
most examples, the code with `use scope' is almost always worse than without
it. I actually agree with that. The code in all but the first example gets
worse with `use scope'. Why I'm proposing it? Because most the code I write
resembles the first example. I usually don't access global variables in my
subs, I mostly don't access file-scoped variables and sometimes I use
closures, but these are very seldom. If I can improve clarity on code I
write more often, I think it's worth.

Note also, I'm not proposing everyone uses it, neither that some use it
always. I myself wouldn't use it always. For quick scripts, for instance, I
don't mind having only global variables (I don't use `my' either). But for
writing modules that essentially need to use lexically scoped variables to
guarantee they won't harm scripts that use those modules, I think it's a
win.

I also see no action at a distance here, since the only way to change the
way a sub sees a variable is inside the sub the variable is being used.
That's where I think the discussion about RFC 64 was problematic. It was
assuming `our' *outside* the block would affect variables inside the block.
And that's AAAD for sure!

Consider also I'm not wanting you to use it, or like it whatsoever. I only
think it would probably be useful for some of us, and that only adding a new
`scope' pragma wouldn't hurt anybody, while possibly helping some. And it
wouldn't be much work to do either, since mostly everything that can be done
with it can be almost directly translated in something that can be done
without it (but hey, let's put the burden on translate it to the compiler,
since it's what it does anyway!).

Sorry again to bother you with this issue again, but I actually think it's
worth trying it.

- Branden

Reply via email to