Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

the $! too global

2 views
Skip to first unread message

Darren Duncan

unread,
Dec 5, 2005, 2:47:20 AM12/5/05
to perl6-l...@perl.org
I've run into a problem today with my Perl 6 coding, which is due to
a perceived design flaw in the current Perl 6 spec (this was
discussed on #perl6 just now, mainly between myself and autrijus), so
I'm bringing it up here.

And yes, autrijus thinks the behaviour I'm seeing in Pugs is
according to an actual spec, and mentioned in S02 with a
"Conjectural:" beside it.

The problem is that $! is being treated too much like a global
variable and not enough like a lexical variable. Consider the
following example:

sub foo () {
try {
die MyMessage.new( 'key' => 'dang', 'vars' => {'from'=>'foo()'} );
};
if ($!) {
display_message( $! );
}
else {
display_message( MyMessage.new(
'key' => 'nuthin_wrong', 'vars' => {'from'=>'foo()'} ) );
}
}

sub display_message ( MyMessage $m ) {
try {
potential_problem(); # on problem, dies
};
if ($!) {
deal_with_it();
}

say "this message has a key {$m.key()}, is from {$m.vars{'from'}}";
}

This is a generified example of my Locale::KeyedText module and its examples.

Under the current system, a subroutine argument is an alias for the
container passed to it; in this case, $e is an alias for the $! that
was set in foo()'s try-block.

Unfortunately, the $! that is set or undeffed by
display_exception()'s try-block appears to be the same container as
foo()'s $!, which means that by the time say() is called, $e has been
wiped out.

Now, I can understand that consequence if the code instead looked like this:

my $*myerr = undef;

sub foo () {
try {
$*myerr = MyMessage.new( 'key' => 'dang', 'vars' => {'from'=>'foo()'} );
};
if ($*myerr) {
display_message ( $*myerr );
}
else {
display_message( MyMessage.new(
'key' => 'nuthin_wrong', 'vars' => {'from'=>'foo()'} ) );
}
}

sub display_message ( MyMessage $m ) {
try {
potential_problem(); # on problem, first sets $*myerr before death
};
if ($*myerr) {
deal_with_it();
}

say "this message has a key {$m.key()}, is from {$m.vars{'from'}}";
}

However, much like $_, I expect that exception handling done within a
called subroutine isn't going to mess up that subroutine's parent
scope, and consequently its own arguments.

Under the current system, I can keep $m preserved by either declaring
that argument with 'is copy', or having foo() explicitly copy its $!
to another lexical var which is then passed to display_exception(),
however, either of those is counter-intuitive and ugly.

First of all, declaring a subroutine's argument 'is copy' presumes
that either you are going to modify its value in the subroutine, or
you will be storing it in an object attribute or something and you
don't want to risk changes to it when the subroutine's caller
modifies the container it just passed.

But in the case of my example, the called subroutine is doing neither
of those things; no changes are made, and $m isn't kept beyond the
subroutine's execution.

Note that, while my examples don't use a CATCH block (which isn't yet
implemented in Pugs), you get the exception from $! or an alias
thereof anyway, so whether or not I used CATCH is factored out of the
discussion at hand here.

Now, I'm not sure exactly of the right solution for this, and perhaps
others such as autrijus can chime in with some help here, but the
current situation needs to be changed. Either $! becomes
semi-lexical, so a called sub's try block doesn't wipe out the $!
from a caller, or $! is treated special when used as a subroutine
argument and is automatically passed by copy, without the subroutine
signiture having to specify 'is copy'.

What I want to have happen is that display_message() works correctly
regardless of whether its argument came from $! or some other source,
without having to declare 'is copy' because of the possibility it is
from $!.

The big problem here is that almost any argument for any subroutine
could potentially come from $!, and it would be rediculous for them
all to be 'is copy' just to handle the possibility.

Nor is it reasonable for a caller to have to copy $! to another
variable before passing it to a subroutine just because that sub
*may* have its own try block ... a caller shouldn't have to know
about the implementation of what it called.

I welcome feedback on this issue and a positive, elegant resolution.

-- Darren Duncan

Luke Palmer

unread,
Dec 5, 2005, 3:38:56 AM12/5/05
to Darren Duncan, perl6-l...@perl.org
On 12/5/05, Darren Duncan <dar...@darrenduncan.net> wrote:
> Under the current system, a subroutine argument is an alias for the
> container passed to it;

The most immediate offender here is the referential passing semantics.
Here is a code case:

sub foo ($x, &code) {
&code();
say $x;
}
my $bar = 41;
foo($bar, { $bar++ });

As far as I recall, the spec says that this should output "42". My
intuition tells me that it should output "41".

In fact, most modern languages (python, java, ...) would consider it
to be "41". They have "assignment" semantics, rather than "binding"
semantics.

What was the reason for choosing binding semantics?

Luke

TSa

unread,
Dec 5, 2005, 6:32:03 AM12/5/05
to perl6-l...@perl.org
HaloO,

Luke Palmer wrote:
> The most immediate offender here is the referential passing semantics.

IIRC, the default is to be a read-only ref. Not even local modifications
are permitted if the 'is copy' property is missing.


> Here is a code case:
>
> sub foo ($x, &code) {
> &code();
> say $x;
> }
> my $bar = 41;
> foo($bar, { $bar++ });
>
> As far as I recall, the spec says that this should output "42". My
> intuition tells me that it should output "41".

My intuition also opts for 41. Evaluating &code() inside foo either
violates the constance of its own $x if the changes propagate back
into the already bound/passed argument or violates the callers
excapsulation. The latter depends on what exactly the second argument
of the call of foo closes over. Theoretically the binding of
CALLER<$bar> to foo<$x> could spill over to the closure which then
would mean foo(x => $bar, { $x++ }) and require $x beeing declared
either 'is copy' or 'is rw'. In other words it could be marked as error.
--
$TSa.greeting := "HaloO"; # mind the echo!

Juerd

unread,
Dec 5, 2005, 6:40:52 AM12/5/05
to perl6-l...@perl.org
TSa skribis 2005-12-05 12:32 (+0100):

> IIRC, the default is to be a read-only ref. Not even local modifications

s/ref/alias/, which you can see as an implicit or automatic reference,
but which we usually don't call that.


Juerd
--
http://convolution.nl/maak_juerd_blij.html
http://convolution.nl/make_juerd_happy.html
http://convolution.nl/gajigu_juerd_n.html

TSa

unread,
Dec 5, 2005, 7:54:09 AM12/5/05
to perl6-l...@perl.org
HaloO,

Darren Duncan wrote:
> The problem is that $! is being treated too much like a global variable
> and not enough like a lexical variable. Consider the following example:

Wasn't the idea to have $! only bound in CATCH blocks?


> sub foo () {
> try {
> die MyMessage.new( 'key' => 'dang', 'vars' => {'from'=>'foo()'} );
> };

CATCH {

> if ($!) {
> display_message( $! );
> }
> else {
> display_message( MyMessage.new(
> 'key' => 'nuthin_wrong', 'vars' => {'from'=>'foo()'} ) );
> }

} # end of CATCH

> }

> Note that, while my examples don't use a CATCH block (which isn't yet
> implemented in Pugs), you get the exception from $! or an alias thereof
> anyway, so whether or not I used CATCH is factored out of the discussion
> at hand here.

Well, I think it brings us back to the discussion about the exception
semantics ;)
A CATCH block is not supposed to be executed under normal circumstances
and $! is undef outside of CATCH blocks. Your code mixes these two
things.


> The big problem here is that almost any argument for any subroutine
> could potentially come from $!, and it would be rediculous for them all
> to be 'is copy' just to handle the possibility.

Oh, you are advocating exception handling while an exception is handled!
Thus after handling the exception exception the one-level-down exception
handler procedes normally exceptional again. Finally the normal,
non-exceptional layer is re-instated, or some such.


> Nor is it reasonable for a caller to have to copy $! to another variable
> before passing it to a subroutine just because that sub *may* have its
> own try block ... a caller shouldn't have to know about the
> implementation of what it called.
>
> I welcome feedback on this issue and a positive, elegant resolution.

I'm still advocating a semantic for exceptions that drop out of the
normal flow of events and call code explicitly dedicated to that task.
Returning to the normal flow as if nothing exceptional happened is
particularly *not* guarenteed! Thus the context of $! is *not* the
runtime context, just like the compile context isn't runtime either.

Actually, many of the exceptions might be compiled-in assertions that
where silenced warnings of the compiler: "didn't I say we'll meet again
:)".

Nicholas Clark

unread,
Dec 5, 2005, 7:55:51 AM12/5/05
to TSa, perl6-l...@perl.org

No, I think not, because the closure on the last line closes over a
read/write variable. It happens that read only reference to the same variable
is passed into the subroutine, but that's fine, because the subroutine never
writes to *its* reference.

Thinking about it in C terms, where pointers and values are explicit, it's as
if function arguments are always passed as const pointers to a value in a
outer scope. This seems fine and consistent to me, but you can never be sure
whether anyone else has another pointer to that same value, which they can
modify at any time.

If nearly all function parameters are PMCs (internally) I don't think that
this is an efficiency problem, as PMCs are always reference semantics, even
when loaded into Parrot's registers.

Nicholas Clark

TSa

unread,
Dec 5, 2005, 9:11:08 AM12/5/05
to perl6-l...@perl.org
HaloO,

Nicholas Clark wrote:
> No, I think not, because the closure on the last line closes over a
> read/write variable. It happens that read only reference to the same variable
> is passed into the subroutine, but that's fine, because the subroutine never
> writes to *its* reference.

So, you argue that Luke's example should print 42?


> Thinking about it in C terms, where pointers and values are explicit, it's as
> if function arguments are always passed as const pointers to a value in a
> outer scope. This seems fine and consistent to me, but you can never be sure
> whether anyone else has another pointer to that same value, which they can
> modify at any time.

Sure. But I consider this a semantic nightmare. And the optimizer thinks
the same. Basically every variable then becomes volatile in C terms :(
Note that lazy evaluation does *not* imply permanent refetching.
Actually the opposite is true, lazy evaluation implies some capturing
of former state as far as the lazied part needs it to produce its
content. And item parameters aren't even lazy by default!


> If nearly all function parameters are PMCs (internally) I don't think that
> this is an efficiency problem, as PMCs are always reference semantics, even
> when loaded into Parrot's registers.

The point I'm argueing about is how exposed subs like Luke's foo should
be to this. Essentially I want the invocation of foo to create a COW
branchpoint in the outer $bar such that when, from where ever, this
value is changed, the former $bar that foo's $x was bound to remains
unharmed. And is garbage collected when foo is done with it.

Larry Wall

unread,
Dec 5, 2005, 12:10:57 PM12/5/05
to perl6-l...@perl.org
My gut-level feeling on this is that $! is going to end up being an
"env" variable like $_. (If you don't know what "env" is then you've
not read the conjectural parts of S2 lately.) Then the problem reduces
to what you do with an unhandled $! at the end of a lexical scope,
which is probably just to fail, insofar as this style of processing is
treating the rest of the block as an implicit catch block. We'd also
have to determine what "handled" means. Probably just evaluating
$! for boolean is sufficient to mark it as handled.

With the env solution, if an inner sub wants to refer to its caller's
$!, it'd use $+! or ENV::<$!> instead of $!. And $! passed to a sub
as an argument would simply be a different variable, just as everyone
gets their own $_ right now.

Larry

TSa

unread,
Dec 7, 2005, 3:43:31 AM12/7/05
to perl6-l...@perl.org
HaloO,

Larry Wall wrote:
> My gut-level feeling on this is that $! is going to end up being an
> "env" variable like $_.

I just re-read about exceptions. Well, I undestand now that $! is
intented as a variable with a spectrum of meanings ranging from

1) the return value of a sub, through
2) an error return value, up to
3) a non-return value.

Of these only the last is what I know as exceptions. I'm for now
undecided to regard these three purposes of $! as very clever or
as big potential for subtle problems. From the viewpoint of the
execution model this environment approach is very clear, though.
It nicely unifies return values with in-band errors and out-band
exceptions. Which leaves just one wish from my side: make CATCH
blocks dispatched. That is they get a signature what kind of
exception they handle. The optimizer will make good use of this
information.


> Then the problem reduces
> to what you do with an unhandled $! at the end of a lexical scope,

I don't understand this. The end of scope is either the closing
brace or an explicit statement like next, leave, return, yield,
fail or die. In the above spectrum of $! these are resultizer
just as given, for and friends are topicalizer for $_. But I think
simple assignment to $! is not foreseen, or is it?

For an example let's assume that / returns 'undef but division by zero'
in $! if its rhs is zero.

{ # begin of scope

my $x = 3;
my $y = 4;

say $x/$y;

$x = foo; # foo could return 0

my $z = $y/$x; # $! == 'undef but division by zero' if $x == 0
# Is that assignable to $z?

$z++; # This is now a statement in a potential CATCH block?

Larry Wall

unread,
Dec 8, 2005, 12:41:56 PM12/8/05
to perl6-l...@perl.org
On Wed, Dec 07, 2005 at 09:43:31AM +0100, TSa wrote:
: HaloO,

:
: Larry Wall wrote:
: >My gut-level feeling on this is that $! is going to end up being an
: >"env" variable like $_.
:
: I just re-read about exceptions. Well, I undestand now that $! is
: intented as a variable with a spectrum of meanings ranging from
:
: 1) the return value of a sub, through
: 2) an error return value, up to
: 3) a non-return value.
:
: Of these only the last is what I know as exceptions. I'm for now
: undecided to regard these three purposes of $! as very clever or
: as big potential for subtle problems. From the viewpoint of the
: execution model this environment approach is very clear, though.
: It nicely unifies return values with in-band errors and out-band
: exceptions. Which leaves just one wish from my side: make CATCH
: blocks dispatched. That is they get a signature what kind of
: exception they handle. The optimizer will make good use of this
: information.

I think that's basically the direction that Parrot is headed.

: >Then the problem reduces


: >to what you do with an unhandled $! at the end of a lexical scope,
:
: I don't understand this. The end of scope is either the closing
: brace or an explicit statement like next, leave, return, yield,
: fail or die. In the above spectrum of $! these are resultizer
: just as given, for and friends are topicalizer for $_. But I think
: simple assignment to $! is not foreseen, or is it?

I don't entirely understand it all yet myself. It's possible that we're
trying to unify too many ideas into $! all at once. It may be impossible
to simultaneously do all of:

* emulate Perl 5's $!, $@, $? and $^E
* represent only the "last" exception status
* never lose exception information

Indeed, the faults with Perl 5's $! mechanism are the major reason
for CATCH blocks. So maybe we should just make $! as close to Perl 5
semantics as possible and then steer people away from it if they
want to write robust code.

Even the first of those items above is something we really haven't
thought through. In Perl 5 $! has errno semantics that mean you
shouldn't rely on its value unless something went "bang" and you know
it some other way. Whereas $@ and $? rely respectively on evalish
and systemish calls to clear the variables on success. Does unifying
$! with those mean that $! has to be cleared on every function call?
That seems rather ill-defined. I think the solution is more along
the lines of $! clears its boolean/defined status on every function
call but remembers the last actual exception. Or maybe we just say
that $! stays true if it contains any exception, and rely on eval
or system-like calls to clear $! if they want test-after semantics?
I can argue it both ways, which probably means that neither solution
is the right one.

: For an example let's assume that / returns 'undef but division by zero'


: in $! if its rhs is zero.

Well, NaN probably, but go on.

: { # begin of scope


:
: my $x = 3;
: my $y = 4;
:
: say $x/$y;
:
: $x = foo; # foo could return 0
:
: my $z = $y/$x; # $! == 'undef but division by zero' if $x == 0
: # Is that assignable to $z?
:
: $z++; # This is now a statement in a potential CATCH block?

: }

Yes, the return value is assignable whether it is NaN or undef
(assuming the type of $z doesn't prevent assignment), but the return
value is not quite the same thing as $!, I suspect. It's more like
$! contains a pointer to the latest error return value. Another
way to look at it is that the returned exception value is immutable
(more or less) but $! is mutably bound to some error value.

Larry

Darren Duncan

unread,
Dec 20, 2005, 9:30:54 PM12/20/05
to perl6-l...@perl.org
Following the discussions from 2 weeks ago, are there any plans to
update the synopsis soon regarding the $! variable? Synopsis 2 still
says that it is conjectural to whether $! is always environmental.
Thanks. -- Darren Duncan
0 new messages