"def" vs "sub" (was RE: More random python observations from a perl programmer)

Tom Christiansen tchrist at jhereg.perl.com
Fri Aug 20 03:04:43 EDT 1999


>That cuts both ways, of course, as does any difference.  That is, I once got
>miffed at Perl because my named, nested functions weren't working.  "man
>pages" or not <wink>, it wasn't until I studied the source that I figured
>out Perl effectively moves all named functions to the top level, and if you
>have two nested functions in a package that happen to have the same name,
>the first gets tossed into the bit bucket:

You didn't localize your function!  Remember, in Perl things
are only local if you say they are.

>$x = 5;

>sub outer1 {
>    my $x = 50;
>    sub inner {
>        print "in the first inner, and here's x: $x\n";
>    }
>    &inner();
>}

>sub outer2 {
>    my $x = 500;
>    sub inner {
>        print "in the second inner, and here's x: $x\n";
>    }
>    &inner();
>}

>&outer1();

That could have been like this:

    sub outer1 {
	my $x = 50;
	local *inner = sub {
	    print "in the first inner, and here's x: $x\n";
	};
	&inner();
    }

    sub outer2 {
	my $x = 500;
	local *inner = sub {
	    print "in the second inner, and here's x: $x\n";
	};
	&inner();
    }

    &outer1();

And run just fine.  BTW, you should have gotten a bucket of frightened
warnings out of the compiler about that.  Syntax checking the compile,
with warnings (somewhat like gcc -Wall -pedantic, but without *.o
generation):

    % perl -wc /tmp/timstuff
    Variable "$x" will not stay shared at /tmp/timstuff line 6.
    Variable "$x" will not stay shared at /tmp/timstuff line 14.
    Subroutine inner redefined at /tmp/timstuff line 13.
    Name "main::x" used only once: possible typo at /tmp/timstuff line 1.
    /tmp/timstuff syntax OK

If that is mysterious, bump up the verbosity:

    % perl -Mdiagnostics -wc /tmp/timstuff

    Variable "$x" will not stay shared at /tmp/timstuff line 6 (#1)
	
	(W) An inner (nested) named subroutine is referencing a lexical
	variable defined in an outer subroutine.

	When the inner subroutine is called, it will probably see the
	value of the outer subroutine's variable as it was before and
	during the *first* call to the outer subroutine; in this case,
	after the first call to the outer subroutine is complete, the
	inner and outer subroutines will no longer share a common value
	for the variable.  In other words, the variable will no longer
	be shared.

	Furthermore, if the outer subroutine is anonymous and references
	a lexical variable outside itself, then the outer and inner
	subroutines will never share the given variable.

	This problem can usually be solved by making the inner subroutine
	anonymous, using the sub {} syntax.  When inner anonymous subs
	that reference variables in outer subroutines are called or
	referenced, they are automatically rebound to the current values
	of such variables.
	
    Variable "$x" will not stay shared at /tmp/timstuff line 14 (#1)

    Subroutine inner redefined at /tmp/timstuff line 13 (#2)
	
	(W) You redefined a subroutine.  To suppress this warning, say
	
	    {
	    local $^W = 0;
	    eval "sub name { ... }";
	    }
	
    Name "main::x" used only once: possible typo at /tmp/timstuff line 1 (#3)

	(W) Typographical errors often show up as unique variable names.
	If you had a good reason for having a unique name, then just
	mention it again somehow to suppress the message.  The use vars
	pragma is provided for just this purpose.
	
    /tmp/timstuff syntax OK

Yes, this local function thing is in the Perl Cookbook in the subroutines
chapter.  Larry said that the next release of Perl *shall support

    sub outer1 {
	my $x = 50;
	my sub inner {
	    print "in the first inner, and here's x: $x\n";
	}
	&inner();
    }

    sub outer2 {
	my $x = 500;
	my sub inner {
	    print "in the second inner, and here's x: $x\n";
	}
	&inner();
    }

    &outer1();

But this is lexical scoping, not dynamic scoping.  With the local
sub inner, inner could call fred, and then fred could still
see inner to call it again in indirect recursion.  That couldn't
happen with my sub inner, which is lexically scoped.

BTW, see what I mean about the importance of good error messages?
I'm used to something more than Ken Thompson's "?" retort.  Everytime I
get NameError from Python, I just want to kick it.

And yes, those were compile-time warnigns.  In the developer
release of Perl, you can promote some or all of those into 
fatals if you want, which will make Guido carp less.

    use warnings qw/FATAL uninitialized numeric/;

is more in the "python" style.  And you can always say

    use warnings qw/FATAL all/;

if you feel all warnings should kill you.  Note that that pragma, like use
strict, is lexically scoped to the surrounding nested scope.  That way
one block can selectively enable classes of warnings.  Warning classes
are: 

    all ambiguous closed closure default deprecated exec io misc newline
    numeric octal once parenthesis pipe precedence printf recursion
    redefine reserved semicolon signal substr syntax taint uninitialized
    unopened unsafe untie utf8 void

And you can do

    use warnings;
    no warnings qw/once exec ambiguous/;

or whatever.

You know, it occurs to me that it might be nice to be able to selectively
promote only compile-time warnings to fatals.  Perl does a lot of
compile-time stuff (including constant expression folding, inlining of
some subroutines, and folding of those results!).

What does Python do at compile-time?

--tom




More information about the Python-list mailing list