Parameter and trait questions - just how 'only' _is_ 'read-only'?

7 views
Skip to first unread message

Chip Salzenberg

unread,
Mar 25, 2005, 1:53:08 PM3/25/05
to Perl 6 Language
I'm working on enhancing Perl6::Subs[*] to support more parameter
traits than just C<is required>. I have some questions about
parameters and traits. (These questions all apply to pure Perl 6,
which I know I won't be able to translate completely, but I want to
know which target I'm missing.)

* Given a parameter C<Array @a>, it's obvious I'm not allowed to
C<push @a,1>, because I didn't say C<is rw>. But am I allowed to
C<@a[0]++>? How about C<@a[0][0]++>? How deep is read-only-ness?

* Similarly, how deep is the copy implied by C<is copy>?

* Do traits attach syntactically to the variable name, or to the
declaration as a whole?

variable: @a is rw of Array
Array @a is rw

declaration: @a of Array is rw
Array @a is rw

* As far as I can tell, the choice of spelling an array parameter
C<Array @a> or C<Array $a> is entirely cosmetic: both @a and
$a are capable of holding an Array reference. Is there actually
a difference, e.g. in how they handle an undefined value?


[*] Shameless Plug: Perl6::Subs is a source filter that lets you use
much of the Perl 6 parameter syntax in your Perl 5 programs; and
it enforces many constraints for you. You can even add your own
constraints with C<where BLOCK> subtyping. Amaze your enemies!
Confound your friends! Use Perl6::Subs today!

--
Chip Salzenberg - a.k.a. - <ch...@pobox.com>
Open Source is not an excuse to write fun code
then leave the actual work to others.

Luke Palmer

unread,
Mar 26, 2005, 5:13:07 AM3/26/05
to Chip Salzenberg, Perl 6 Language
Chip Salzenberg writes:
> I'm working on enhancing Perl6::Subs[*] to support more parameter
> traits than just C<is required>. I have some questions about
> parameters and traits. (These questions all apply to pure Perl 6,
> which I know I won't be able to translate completely, but I want to
> know which target I'm missing.)
>
> * Given a parameter C<Array @a>, it's obvious I'm not allowed to
> C<push @a,1>, because I didn't say C<is rw>. But am I allowed to
> C<@a[0]++>? How about C<@a[0][0]++>? How deep is read-only-ness?

I believe that the default constant parameters is so we don't have to
construct lvalues out of our arguments when we call. So that probably
means that it's very shallow.

On the language level, I've been thinking that it would be good to go
C's way and not allow any parameter modification whatsoever. The
problem is that in the presence of methods, we can't tell whether we
have to lvaluize anymore, so we're back to the Perl 5 trap of lvaluizing
everything.

So if you want things modified, you'd have to pass in a reference.
Arrays and hashes would not generally have this restriction, since we
pass references of those guys anyway.

> * Similarly, how deep is the copy implied by C<is copy>?

I think it's exactly as deep as read-only-ness.

> * Do traits attach syntactically to the variable name, or to the
> declaration as a whole?
>
> variable: @a is rw of Array
> Array @a is rw
>
> declaration: @a of Array is rw
> Array @a is rw

Well, from this example it seems like `of` should be tighter than `is`.

> * As far as I can tell, the choice of spelling an array parameter
> C<Array @a> or C<Array $a> is entirely cosmetic: both @a and
> $a are capable of holding an Array reference. Is there actually
> a difference, e.g. in how they handle an undefined value?

Hmmm... well I think all scalars are allowed to be undef. Arrays
aren't. So yeah, if you give @a undef, it probably gives you [] (or
croaks, but I don't think that's a good idea). If you give $a undef, it
gives you undef.

> [*] Shameless Plug: Perl6::Subs is a source filter that lets you use
> much of the Perl 6 parameter syntax in your Perl 5 programs; and
> it enforces many constraints for you. You can even add your own
> constraints with C<where BLOCK> subtyping. Amaze your enemies!
> Confound your friends! Use Perl6::Subs today!

Cool.

Luke

Rod Adams

unread,
Mar 26, 2005, 2:45:45 PM3/26/05
to Perl 6 Language
Chip Salzenberg wrote:

> * As far as I can tell, the choice of spelling an array parameter
> C<Array @a> or C<Array $a> is entirely cosmetic: both @a and
> $a are capable of holding an Array reference. Is there actually
> a difference, e.g. in how they handle an undefined value?
>
>

Uhm... It was my impression that one of those creates an Array of
Arrays, and the other just an Array. In other words, using @ instead of
$ puts a "Array of" in front of the supplied type.

This makes sense when one considers orthogonality with C<Int @a> and
C<Int $a>. But it's easy to get tripped up it.

-- Rod Adams

(Who needs more days in the week, so he can continue work on S29).

Chip Salzenberg

unread,
Mar 26, 2005, 3:45:30 PM3/26/05
to Rod Adams, Perl 6 Language
According to Rod Adams:

> Chip Salzenberg wrote:
> >* As far as I can tell, the choice of spelling an array parameter
> > C<Array @a> or C<Array $a> is entirely cosmetic: both @a and
> > $a are capable of holding an Array reference. Is there actually
> > a difference, e.g. in how they handle an undefined value?
>
> Uhm... It was my impression that one of those creates an Array of
> Arrays, and the other just an Array.

Ah, my question had a bug. What I meant was:

* As far as I can tell, the choice of spelling an array parameter

C<@a> or C<Array $a> is entirely cosmetic: both @a and $a are


capable of holding an Array reference. Is there actually a
difference, e.g. in how they handle an undefined value?

--

Larry Wall

unread,
Mar 26, 2005, 7:54:47 PM3/26/05
to Perl 6 Language
On Sat, Mar 26, 2005 at 03:45:30PM -0500, Chip Salzenberg wrote:
: According to Rod Adams:

: > Chip Salzenberg wrote:
: > >* As far as I can tell, the choice of spelling an array parameter
: > > C<Array @a> or C<Array $a> is entirely cosmetic: both @a and
: > > $a are capable of holding an Array reference. Is there actually
: > > a difference, e.g. in how they handle an undefined value?
: >
: > Uhm... It was my impression that one of those creates an Array of
: > Arrays, and the other just an Array.
:
: Ah, my question had a bug. What I meant was:
:
: * As far as I can tell, the choice of spelling an array parameter
: C<@a> or C<Array $a> is entirely cosmetic: both @a and $a are
: capable of holding an Array reference. Is there actually a
: difference, e.g. in how they handle an undefined value?

Not really. The main difference is that @a will flatten in a list
context, and $a won't.

That being said, in Perl 5, if you say

@a = undef;

you don't get an undefined array. I'd like to make undef smart enough
about list contexts that @a actually does end up undefined in Perl 6.
That is, in scalar context, undef is a scalar value as in Perl 5, but
in Perl 6, undef in list context means "there isn't anything here if
you try to look for it", so it's more like () in Perl 5, except that
it also undefines the array if it's the only thing in the array.

I don't know if that's an entirely consistent semantics, but I'd rather
have a little inconsistency and preserve error informaiton for later
debugging whenever possible, and that undef you tried to initialize the
array with might have some interesting commentary in it. (Though, of
course, my example above is a rather boring value of undef.)

Larry

Larry Wall

unread,
Mar 26, 2005, 8:12:20 PM3/26/05
to Perl 6 Language
On Sat, Mar 26, 2005 at 03:13:07AM -0700, Luke Palmer wrote:
: Chip Salzenberg writes:
: > I'm working on enhancing Perl6::Subs[*] to support more parameter
: > traits than just C<is required>. I have some questions about
: > parameters and traits. (These questions all apply to pure Perl 6,
: > which I know I won't be able to translate completely, but I want to
: > know which target I'm missing.)
: >
: > * Given a parameter C<Array @a>, it's obvious I'm not allowed to
: > C<push @a,1>, because I didn't say C<is rw>. But am I allowed to
: > C<@a[0]++>? How about C<@a[0][0]++>? How deep is read-only-ness?
:
: I believe that the default constant parameters is so we don't have to
: construct lvalues out of our arguments when we call. So that probably
: means that it's very shallow.
:
: On the language level, I've been thinking that it would be good to go
: C's way and not allow any parameter modification whatsoever. The
: problem is that in the presence of methods, we can't tell whether we
: have to lvaluize anymore, so we're back to the Perl 5 trap of lvaluizing
: everything.

I think only predeclared simple subs are allowed to have "rw" parameters.
Methods can have "is ref" parameters, but those don't require enforced
lvaluehood on the caller end like rw does.

: So if you want things modified, you'd have to pass in a reference.


: Arrays and hashes would not generally have this restriction, since we
: pass references of those guys anyway.

Yes.

: > * Similarly, how deep is the copy implied by C<is copy>?


:
: I think it's exactly as deep as read-only-ness.

And both may be exactly as deep as COW.

: > * Do traits attach syntactically to the variable name, or to the


: > declaration as a whole?
: >
: > variable: @a is rw of Array
: > Array @a is rw
: >
: > declaration: @a of Array is rw
: > Array @a is rw
:
: Well, from this example it seems like `of` should be tighter than `is`.

Traits are not allowed on ordinary rvalues, only on declarations. "is"
traits always attach to the main declaration. "of" types always attach
to the container on their immediate left.

: > * As far as I can tell, the choice of spelling an array parameter


: > C<Array @a> or C<Array $a> is entirely cosmetic: both @a and
: > $a are capable of holding an Array reference. Is there actually
: > a difference, e.g. in how they handle an undefined value?
:
: Hmmm... well I think all scalars are allowed to be undef. Arrays
: aren't. So yeah, if you give @a undef, it probably gives you [] (or
: croaks, but I don't think that's a good idea). If you give $a undef, it
: gives you undef.

As I mentioned in my other message, I think we should not assume that
Perl 6 works the same in this regard as Perl 5 does. There needs to be
something we can return that not only means (), but means also means
"You're hosed! (And here's why.)" And I think we can make undef mean
that if we make it lazily sensitive to scalar/list context (much like @a
itself can be lazily sensitive to context).

Hmm, maybe it would simpler to just tell everyone undef is a special empty
lazy array that refuses to produce a value no matter how you ask.

Larry

Uri Guttman

unread,
Mar 26, 2005, 11:57:48 PM3/26/05
to Perl 6 Language
>>>>> "LW" == Larry Wall <la...@wall.org> writes:

LW> That being said, in Perl 5, if you say

LW> @a = undef;

LW> you don't get an undefined array. I'd like to make undef smart enough
LW> about list contexts that @a actually does end up undefined in Perl 6.
LW> That is, in scalar context, undef is a scalar value as in Perl 5, but
LW> in Perl 6, undef in list context means "there isn't anything here if
LW> you try to look for it", so it's more like () in Perl 5, except that
LW> it also undefines the array if it's the only thing in the array.

then how would you assign undef to the only element of the array? would this
be needed:

@a = ( undef ) ; # same as p5?

vs.
@a = undef ; # like undef @a in p5?

i have always railed against undef on aggregates as it leads to using
defined on them which is not the same as checking if an aggregate has
any elements. i see that often in newbie code. in fact i would like to
stop allowing undef as a function with args and have it only return a
scalar undef value. there should be a different op to truly make an
aggregate undefined (and i still don't see a need for that, emptying it
is all that i ever think is needed).

in my world undef is a scalar value and nothing else. how do you see it
in p6?

uri

--
Uri Guttman ------ u...@stemsystems.com -------- http://www.stemsystems.com
--Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org

Uri Guttman

unread,
Mar 27, 2005, 12:04:39 AM3/27/05
to Perl 6 Language
>>>>> "LW" == Larry Wall <la...@wall.org> writes:

LW> As I mentioned in my other message, I think we should not assume that
LW> Perl 6 works the same in this regard as Perl 5 does. There needs to be
LW> something we can return that not only means (), but means also means
LW> "You're hosed! (And here's why.)" And I think we can make undef mean
LW> that if we make it lazily sensitive to scalar/list context (much like @a
LW> itself can be lazily sensitive to context).

LW> Hmm, maybe it would simpler to just tell everyone undef is a special empty
LW> lazy array that refuses to produce a value no matter how you ask.

why use undef for the error code? isn't this what exceptions are for? or
setting $!? i actually use naked return as a postive thing in stem
(string return values are bad and have the error string. it is
consistant so it works). the problem with returning undef (or naked
return) is that it is in-band data. now you could do a naked return but
error thing. and then the called has to check for that property each
time. but what does that mean when you do this (bad p6 code):

sub return_error { return but error }
my @a = return_error() ;

is @a empty or what? how do you see the error in @a?

i just don't like seeing undef used for error handling as it has too
many other uses (even if i did it in stem). just make undef a scalar
value and not a function nor a error marker.

Larry Wall

unread,
Mar 27, 2005, 12:41:09 AM3/27/05
to Perl 6 Language
On Sun, Mar 27, 2005 at 12:04:39AM -0500, Uri Guttman wrote:
: >>>>> "LW" == Larry Wall <la...@wall.org> writes:
:
: LW> As I mentioned in my other message, I think we should not assume that
: LW> Perl 6 works the same in this regard as Perl 5 does. There needs to be
: LW> something we can return that not only means (), but means also means
: LW> "You're hosed! (And here's why.)" And I think we can make undef mean
: LW> that if we make it lazily sensitive to scalar/list context (much like @a
: LW> itself can be lazily sensitive to context).
:
: LW> Hmm, maybe it would simpler to just tell everyone undef is a special empty
: LW> lazy array that refuses to produce a value no matter how you ask.
:
: why use undef for the error code?

Because there's usually a reason why the undef was created, and it would
be useful for the user to learn that.

: isn't this what exceptions are for?

Undef is a form of unthrown exception. Throwing things is often too
violent for fail-soft situation.

: or setting $!?

$! is the current unthrown exception in Perl 6. (Or actually, it's also
the thrown exception inside a CATCH block.)

: i actually use naked return as a postive thing in stem


: (string return values are bad and have the error string. it is
: consistant so it works).

Okay, so in this case you could hack around the problem because you don't
want to return interesting "good" values. But sometimes you have both
interesting good values and interesting bad values. "Interesting" values
of undef solves that problem without having to send either the good data
or the bad data out of band.

: the problem with returning undef (or naked


: return) is that it is in-band data. now you could do a naked return but
: error thing. and then the called has to check for that property each
: time.

You should be checking your return values anyway, or useing "fatal", which
turns these unthrown exceptions into thrown ones.

: but what does that mean when you do this (bad p6 code):


:
: sub return_error { return but error }

That's written:

sub return_error { fail "What went wrong" }

which has the effect of

sub return_error { return undef but Exception("What went wrong") }

unless the caller uses "fatal", in which case it's more like

sub return_error { die("What went wrong") }

: my @a = return_error() ;


:
: is @a empty or what? how do you see the error in @a?

I am proposing that assigning a exceptional undef value (such
as that returned by fail() above) to an array causes the array to
become undefined. If you use that array somewhere else, it's as if
you used the original unthrown exception. Eventually you get caught
trying to do something defined with the undefined value and you get
a nice message out saying where the original undefined value came from.

: i just don't like seeing undef used for error handling as it has too


: many other uses (even if i did it in stem).

I don't see how the proposed semantics interfere with any other uses of
undef. A bare scalar undef is still a very simple value. I don't see
how you can simultaneously argue that it has many uses and yet has only
one simple value.

: just make undef a scalar value and not a function nor a error marker.

fail("Language designer not persuaded"); # :-)

Larry

Larry Wall

unread,
Mar 27, 2005, 1:27:54 AM3/27/05
to Perl 6 Language
On Sat, Mar 26, 2005 at 11:57:48PM -0500, Uri Guttman wrote:
: >>>>> "LW" == Larry Wall <la...@wall.org> writes:
:
: LW> That being said, in Perl 5, if you say
:
: LW> @a = undef;
:
: LW> you don't get an undefined array. I'd like to make undef smart enough
: LW> about list contexts that @a actually does end up undefined in Perl 6.
: LW> That is, in scalar context, undef is a scalar value as in Perl 5, but
: LW> in Perl 6, undef in list context means "there isn't anything here if
: LW> you try to look for it", so it's more like () in Perl 5, except that
: LW> it also undefines the array if it's the only thing in the array.
:
: then how would you assign undef to the only element of the array? would this
: be needed:
:
: @a = ( undef ) ; # same as p5?
:
: vs.
: @a = undef ; # like undef @a in p5?

Those would do the same thing under the current proposal, since
they're both in list context. If you really, really want a scalar
undef value in list context, you could always say

@a = scalar(undef);

: i have always railed against undef on aggregates as it leads to using


: defined on them which is not the same as checking if an aggregate has
: any elements. i see that often in newbie code.

Well, let's not confuse Perl 5's shortcomings with Perl 6's. I think
we should fix the problem by making undef work a little more like the
newbie expects, which is context dependent.

: in fact i would like to


: stop allowing undef as a function with args and have it only return a
: scalar undef value. there should be a different op to truly make an
: aggregate undefined (and i still don't see a need for that, emptying it
: is all that i ever think is needed).

We could certainly split out a separate undefine() function. We could
even give it an optional argument that says *why* it's undefined, turning
it into an unthrown exception, basically. We could use such a function
to create interesting values of undef that are context sensitive.

: in my world undef is a scalar value and nothing else. how do you see it
: in p6?

undef is not a scalar value, it is the explicit *absence* of a value
where you expected one. In Perl 6, undef is the Bearer of Bad News.

If you merely want undef for a scalar placeholder without negative
connotations, I'd suggest something like

class Empty {}
my $nada = new Empty;

instead. And $nada is the same length as undef. $X would be even
shorter.

On the other hand, Perl 6 is consistently returning booleans as
0 and 1 rather than "" and 1, so it's likely that constant 0 and 1
can be stored in "less than a bit", as it were. So there might even
be some kind of zero type that's essentially a value that's always 0,
and takes no room to store at all. That might serve as a pretty good
neutral placeholder.

Larry

Uri Guttman

unread,
Mar 27, 2005, 1:38:38 AM3/27/05
to Perl 6 Language
>>>>> "LW" == Larry Wall <la...@wall.org> writes:

LW> : then how would you assign undef to the only element of the
LW> array? would this : be needed:

LW> :
LW> : @a = ( undef ) ; # same as p5?
LW> :
LW> : vs.
LW> : @a = undef ; # like undef @a in p5?

LW> Those would do the same thing under the current proposal, since
LW> they're both in list context. If you really, really want a scalar
LW> undef value in list context, you could always say

LW> @a = scalar(undef);

that works. i am starting to see what you mean by undef knowing about
context.

LW> : in fact i would like to
LW> : stop allowing undef as a function with args and have it only return a
LW> : scalar undef value. there should be a different op to truly make an
LW> : aggregate undefined (and i still don't see a need for that, emptying it
LW> : is all that i ever think is needed).

LW> We could certainly split out a separate undefine() function. We could
LW> even give it an optional argument that says *why* it's undefined, turning
LW> it into an unthrown exception, basically. We could use such a function
LW> to create interesting values of undef that are context sensitive.

that split makes sense as you are now using undef as a special (or as
you say below unexpected) value. so it shouldn't also be overloaded as a
function operating on variables. just doing the split will make me
happier (if you are so benevolent as to care about my happiness :).

LW> : in my world undef is a scalar value and nothing else. how do you see it
LW> : in p6?

LW> undef is not a scalar value, it is the explicit *absence* of a value
LW> where you expected one. In Perl 6, undef is the Bearer of Bad News.

oy! i feel the pain of the late night phone call. :)

Thomas Sandlaß

unread,
Mar 29, 2005, 12:41:06 PM3/29/05
to Perl 6 Language
Luke Palmer wrote:
> So if you want things modified, you'd have to pass in a reference.
> Arrays and hashes would not generally have this restriction, since we
> pass references of those guys anyway.

But I would really like to see a declaration of any possible modification
in the interface of a sub. Otherwise you might find quite surprising things
in your array, or terribly miss things. So after calling a sub that takes
an array parameter the array argument should be in exactly the same state
as before the call! And any attempt to use modifying operations in the sub
should result in a compile time error. This is good for the optimizer as well.

I don't see the above as a restriction. There are easy ways to achieve
modifyable values by 'is copy' for temporary modifications in the sub and
'is rw' and 'is ref' for full access. But the caller clearly sees that in
the signature. And the typechecker might throw exceptions when type
safety can't be maintained through the sub call:

sub foo (@a is rw) { @a[7] = "heaven" } # type of "" is Str

my Int @i;

foo( @i ); # type error/warning

foo( @i as Array of Any ); # programmer takes responsibility

foo( \@i ); # I'm not sure about that, but could be short of the above
--
TSa (Thomas Sandlaß)

Luke Palmer

unread,
Mar 29, 2005, 6:00:01 PM3/29/05
to Thomas Sandlaß, Perl 6 Language
Thomas Sandlaß writes:
> Luke Palmer wrote:
> >So if you want things modified, you'd have to pass in a reference.
> >Arrays and hashes would not generally have this restriction, since we
> >pass references of those guys anyway.
>
> But I would really like to see a declaration of any possible modification
> in the interface of a sub.

Of course. We already have this.

> Otherwise you might find quite surprising things in your array, or
> terribly miss things. So after calling a sub that takes an array
> parameter the array argument should be in exactly the same state as
> before the call! And any attempt to use modifying operations in the
> sub should result in a compile time error. This is good for the
> optimizer as well.
>
> I don't see the above as a restriction. There are easy ways to achieve
> modifyable values by 'is copy' for temporary modifications in the sub
> and 'is rw' and 'is ref' for full access. But the caller clearly sees
> that in the signature.

Unless the caller can't see the signature, as is the case with methods.
That's the point of my thinking. Perl 5 had a performance (and a slight
semantic) problem with this. It had to construct lvalues out of all
arguments (for which that made sense) because the sub might modify them.
We'll still have to do that unless the "rw"-ness is marked somehow on
the caller's side.

> And the typechecker might throw exceptions when type safety can't be
> maintained through the sub call:
>
> sub foo (@a is rw) { @a[7] = "heaven" } # type of "" is Str
>
> my Int @i;
>
> foo( @i ); # type error/warning

Again, this can't be done unless you know the signature. And in fact,
we can't do type inference on methods unless we do type inference
everywhere, which we can't do if we want an autoloader.

Luke

Thomas Sandlaß

unread,
Mar 30, 2005, 5:05:12 AM3/30/05
to Perl 6 Language
Luke Palmer wrote:
> Unless the caller can't see the signature, as is the case with methods.
> [..]

> Again, this can't be done unless you know the signature. And in fact,
> we can't do type inference on methods unless we do type inference
> everywhere, which we can't do if we want an autoloader.

This sounds to me even messier than the C header file mishap,
or even the old K&R days without protoyped declarations!

How can the programmer know about the signature of a method?
I guess from some documentation. And the prime documentation of
an interface is a formal interface definition provided by the
language. This is e.g. what Ada does. Also CORBA is build around
its IDL (Interface Definition Language) and from version 3.0
onwards a CDL (Component Definition Language).

If I understand you correctly the use statement is more like a
linker/loader directive than a compile time interface include?
BTW, is it foreseen to have a clear conceptual split between the
compiler and the loader? Or is that all blurred?
--
TSa (Thomas Sandlaß)


Luke Palmer

unread,
Mar 30, 2005, 11:40:26 AM3/30/05
to Aaron Sherman, Perl6 Language List
Aaron Sherman writes:

> On Tue, 2005-03-29 at 16:00 -0700, Luke Palmer wrote:
>
> > Unless the caller can't see the signature, as is the case with methods.
>
> I need to understand this piece.
>
> In this code:
>
> class X {
> method meth() {...}
> }
> class Y is X {
> method meth() is rw(int $r) {...}
> }
> sub foo { return my Y $tmp }
> my X $a = foo();
> $a.meth() = 8;
>
> What happens? Do we impose a compiler-level restriction on the call
> that prevents this, or do we always allow it, and let the run-time
> take care of it?
>
> My preference by far is to let the compiler tell you that it's illegal
> because there's no valid reason for a sub-class to change the
> semantics of a polymorphically invoked method like this. The sub-class
> might introduce new semantics, but when manipulated as a base-class
> those semantics should only come into play when they are invoked via
> the interface of the base class. That sounds like an almost axiomatic
> truism to me, but if I read what you wrote correctly, you don't seem
> to agree.

No, I think I agree with you here. But what happens if you change
you're second-to-last line to:

my $a = foo();
$a.meth() = 8;

Perl 6 is both a statically typed language and a dynamically typed
language, and the problems that I am addressing are mostly about the
dynamic part.

> You can combine and autoloader with type inference. You may not choose
> to, but it's not impossible. You can load the signatures ahead of time,
> and link the code at run-time. What gets dicy is this:
>
> eval "use X";
> my X $a;
> $a.m(1);
>
> I think that's invalid too, but I'm not sure.

Again, that's invalid in the static world, but it should work if you
s/my X/my/. The kinds of type inferrence people are saying would solve
our problems require us to infer code like this, which we can't do.

There _is_ a way to do it, actually, but we need to really screw around
with what kinds of things are inferred. In the case:

my $a;
$a.m(1);

We assign the type "objects with an 'm' method that can take a single
integer" to $a. This is a category of objects that spans many classes,
and does not fit into a nice little tree. I think that before we take
on such an idea, we should look for research about this kind of type
inference.

Luke

Aaron Sherman

unread,
Mar 30, 2005, 11:11:36 AM3/30/05
to Luke Palmer, Perl6 Language List
On Tue, 2005-03-29 at 16:00 -0700, Luke Palmer wrote:

> Unless the caller can't see the signature, as is the case with methods.

I need to understand this piece.

In this code:

class X {
method meth() {...}
}
class Y is X {
method meth() is rw(int $r) {...}
}
sub foo { return my Y $tmp }
my X $a = foo();
$a.meth() = 8;

What happens? Do we impose a compiler-level restriction on the call that
prevents this, or do we always allow it, and let the run-time take care
of it?

My preference by far is to let the compiler tell you that it's illegal
because there's no valid reason for a sub-class to change the semantics
of a polymorphically invoked method like this. The sub-class might
introduce new semantics, but when manipulated as a base-class those
semantics should only come into play when they are invoked via the
interface of the base class. That sounds like an almost axiomatic truism
to me, but if I read what you wrote correctly, you don't seem to agree.

It's important to look at this as a two(more?)-stage process with
different agendas and limitations, no?

More generally, I think it's always a valid thing for the compiler to
impose the restrictions of the type it knows about, regardless of what
polymorphism might discover later on at run-time. This means that you
can't do this:

class X { }
class Y { method m (int $a) {...} }
my X $ob = get_me_a_Y();
$ob.m(1);

because X does not provide an m.

> Again, this can't be done unless you know the signature. And in fact,
> we can't do type inference on methods unless we do type inference
> everywhere, which we can't do if we want an autoloader.

You can combine and autoloader with type inference. You may not choose

Larry Wall

unread,
Mar 30, 2005, 12:12:01 PM3/30/05
to Perl6 Language List
On Wed, Mar 30, 2005 at 09:40:26AM -0700, Luke Palmer wrote:
: There _is_ a way to do it, actually, but we need to really screw around

: with what kinds of things are inferred. In the case:
:
: my $a;
: $a.m(1);
:
: We assign the type "objects with an 'm' method that can take a single
: integer" to $a. This is a category of objects that spans many classes,
: and does not fit into a nice little tree.

I think it's perfectly fine for the compiler to make use of whatever
information it has. The trick is to never make any unwarranted
assumptions, such as "Nobody will ever add another class with an 'm'
method." It can assume that only if you explicitly tell it to, and
if nobody has jimmied any classes to remain open and/or non-final.
Otherwise you have to at least be prepared to recalculate the
effective meaning of your 'm' type, and know when that recalculation
is necessary. Even if classes are open and non-final, you can
at least cache information that is temporarily true, and make good
use of it. Perl 5 certainly takes that approach in spots. Perl 5
doesn't do a good job of localizing the damage, however. If you
monkey around with your classes, it basically throws away all the
cached data by incrementing a generation count.

: I think that before we take on such an idea, we should look for


: research about this kind of type inference.

As long as "we" includes "you", that's fine by "me". :-)

Larry

Thomas Sandlaß

unread,
Mar 30, 2005, 1:17:40 PM3/30/05
to Perl6 Language List
HaloO Luke,

you wrote:
> No, I think I agree with you here. But what happens if you change
> you're second-to-last line to:
>
> my $a = foo();
> $a.meth() = 8;
>
> Perl 6 is both a statically typed language and a dynamically typed
> language, and the problems that I am addressing are mostly about the
> dynamic part.

My state of affairs is that these two lines of code lack declarations
that clearly announce what the user of foo and X wanted:
1) &foo returns an X
2) an X does .meth()

Where, how and if these are implemented, autoloaded or what not
is another business. Client side type checking ensures that no
usage outside the declared behaviour occurs. For larger declarations
I which to put them into an interface module, package, header or
however that is called and include it.

On the implementation side we get the complementary definitions:
1) here's an X that does .meth()
2) here's a &foo that returns an X

The dynamism of Perl6 should revolve around how to bring these
two sides of an interface together conveniently and efficiently.
Just using 1% of a big package shouldn't let your 10-liner
become a bloated pig!

And of course the builtin functionality and the packages available
from CPAN save the typical small scale programmer from extensive
declarations. But to use a complex module you have to read
documentation to get the idea to call .meth() in the first place.
And then I like to get the hint from the compiler that there is
no .mth() and happily correct the spelling error. Spotting these
types of errors is were computers are much better than humans :)


> We assign the type "objects with an 'm' method that can take a single
> integer" to $a. This is a category of objects that spans many classes,
> and does not fit into a nice little tree. I think that before we take
> on such an idea, we should look for research about this kind of type
> inference.

Type calculations are not inherently difficult, especially when junctive
values are at hand. The problem is that the inference can quickly diverge
to the useless "something is done to an anything". Dwimmery works best
when it bridges little gaps in an otherwise very rich semantic network.
--
TSa (Thomas Sandlaß)


Aaron Sherman

unread,
Mar 30, 2005, 1:43:48 PM3/30/05
to Luke Palmer, Perl6 Language List
On Wed, 2005-03-30 at 11:40, Luke Palmer wrote:

> No, I think I agree with you here. But what happens if you change
> you're second-to-last line to:
>
> my $a = foo();
> $a.meth() = 8;
>
> Perl 6 is both a statically typed language and a dynamically typed
> language, and the problems that I am addressing are mostly about the
> dynamic part.

The way I've always thought about that one (and perhaps I'm just
misguided) is that you're really saying:

my unknown $a = foo();
$a.meth() = 8;

And unknown does something strange (unique?) along the lines of:

class unknown is scalar, pragma::everything {...}

That is, logically, unknown allows you to use any interface (note that I
said "pragma" because sub-classes of scalar don't get this behavior),
but realistically, a compiler must simply throw up its hands on that ONE
type of variable and allow you to get away with nearly anything (and
leave it up to the run-time to use your vtable to discover the truth).

The feature you get here is that a compiler that wanted to spend hours
of compute-time validating your program COULD search every known
interface, and tell you:

Unknown method signature in dynamic call at line 100

or an even smarter compiler could say:

Method signature on line 100 unrelated to previous usage on line 98

That is, you called a method on line 98 that matches no classes above,
below or the same as the classes matched by the signature of the method
you called on line 100. This kind of "lint" checking would be very
handy, though I'm sure it would be too time-consuming for the general
"-w" case.

> > eval "use X";
> > my X $a;
> > $a.m(1);
> >
> > I think that's invalid too, but I'm not sure.
>
> Again, that's invalid in the static world, but it should work if you
> s/my X/my/. The kinds of type inferrence people are saying would solve
> our problems require us to infer code like this, which we can't do.

I think there, you are well justified in saying that you can try to get
away with it, but a compiler that's smart enough to realize that you
need a run-time loaded dynamic type to make it work should punt it. Of
course, that ASSUMES that auto-loading is a two-stage process with an
interface definition and a run-time code load. Given that, what you
wanted was auto-loading, not run-time string eval. Those are not the
same thing.

If you're doing code-generation at run-time ... huh, what happens, let
me think...

Let's say you generate this code from a specification of some sort:

class __c001 { method __m001(int $__i001) {...} }
__c001.new;

and then try to call:

(eval $generated_code).__m001(1);

Ok, that's good because eval returns the same unknown scalar type as a
bare my declares. I guess you can also taint anything that comes from an
eval and just avert your gaze... you'll have to taint objects that use
non-Perl6 object systems in the same way, anyhoo:

my $thing = Runtime::Python::re::compile('.*');
$thing.search('moo');

> There _is_ a way to do it, actually, but we need to really screw around
> with what kinds of things are inferred. In the case:
>
> my $a;
> $a.m(1);
>
> We assign the type "objects with an 'm' method that can take a single
> integer" to $a. This is a category of objects that spans many classes,
> and does not fit into a nice little tree. I think that before we take
> on such an idea, we should look for research about this kind of type
> inference.

Heh well, here's where we notice that Aaron reads and replies in series
by paragraph ;-)

--
Aaron Sherman <a...@ajs.com>
Senior Systems Engineer and Toolsmith
"It's the sound of a satellite saying, 'get me down!'" -Shriekback


Thomas Sandlaß

unread,
Mar 30, 2005, 1:51:06 PM3/30/05
to Perl6 Language List
Larry Wall wrote:
> I think it's perfectly fine for the compiler to make use of whatever
> information it has. The trick is to never make any unwarranted
> assumptions, such as "Nobody will ever add another class with an 'm'
> method."

Er, isn't that not just the wrong way around? The point is to do the
bookkeeping that an object is needed that does .meth() and that it
is stored in $a, and to complain when that is not the case when it
should be. The earlier the better.

I don't understand why writing 'my X $a' needs at least a declaration
of X, but then '$a.meth()' does not consider '.meth' as a bare method
that needs a declaration in X? Polymorphism is about requiring that
objects do compare and then sort a bunch of them using that particular
role---not more not less. Or think of US Postal just transporting letters
irrespective of them containing Anthrax or not, as long as they bear the
right stamps!
--
TSa (Thomas Sandlaß)


Luke Palmer

unread,
Mar 30, 2005, 1:53:04 PM3/30/05
to Thomas Sandlaß, Perl6 Language List
Thomas Sandlaß writes:
> And of course the builtin functionality and the packages available
> from CPAN save the typical small scale programmer from extensive
> declarations. But to use a complex module you have to read
> documentation to get the idea to call .meth() in the first place.
> And then I like to get the hint from the compiler that there is
> no .mth() and happily correct the spelling error. Spotting these
> types of errors is were computers are much better than humans :)

And it sure would be able to spot that kind of error, supposing the sub
declaration is in scope. But suppose it weren't. Maybe that's a bit
hard to fathom if you haven't written an autoloader. Suppose, then,
that you have this code:

class CodeProxy {
has Code $.code is rw;
sub call ($a) {
$.code($a);
}
}

This is valid Perl 6, and anyone who says otherwise (because of type
signatures) is changing the Perl philosophy too much to be taken
seriously. Now, tell me at compile time whether the call inside `call`
is valid.

Of course, the alleged type inferencer can do some magic and make sure
that it is. It says that the $.code member variable is not just of type
`Code`, but of type `Code is sig($x) forall $x` (are siglets still
around?). Then this code would not fail to compile, but any code that
tried to assign an incompatible routine to the .code member would fail
to compile.

At runtime, you load in a new subclass of `CodeProxy`, `DoubleCodeProxy`
(this programmer apparently doesn't know that we support variadic
signatures :-)

class DoubleCodeProxy is CodeProxy {
method call ($a, +$b) {
.code().($a, $b);
}
}

Again, undisputably valid Perl 6. Every method takes an implicit `*%`,
so it's not an interface violation to add a new named parameter. And
now it's okay to assign a code object to $.code which manditorily takes
two parameters, if that object happens to be a `DoubleCodeProxy`. But
we can't tell whether it's a `DoubleCodeProxy` by type inference,
because we didn't even know what a `DoubleCodeProxy` was at compile
time.

C++ gets around this problem by disallowing a declaration like `has Code
$.code` -- All declarations must be fully specified. Perl is not going
to do that.

Unless I've mis-argued somewhere (I've been straining to get this
message done before my next class starts, so my thinking may be sloppy),
this example case requires the check to be put off until runtime. In
that case, you certainly can't use inferred type information to make any
semantic choices, and the best we can do is to use it as a compile-time
error checker (in the cases where it's capable of doing so) and as an
optimizer.

Luke

Aaron Sherman

unread,
Mar 30, 2005, 1:52:41 PM3/30/05
to Thomas Sandlaß, Perl6 Language List
On Wed, 2005-03-30 at 13:17, Thomas Sandlaß wrote:
> HaloO Luke,
>
> you wrote:
> > No, I think I agree with you here. But what happens if you change
> > you're second-to-last line to:
> >
> > my $a = foo();
> > $a.meth() = 8;
> >
> > Perl 6 is both a statically typed language and a dynamically typed
> > language, and the problems that I am addressing are mostly about the
> > dynamic part.
>
> My state of affairs is that these two lines of code lack declarations
> that clearly announce what the user of foo and X wanted:
> 1) &foo returns an X

No, that was most of the point. &foo did not declare a return type, and
while my code was simplistic, we obviously cannot be certain what &foo
might return in the general case.

Given that, Luke was making the point that $a had not explicit type.

Aaron Sherman

unread,
Mar 30, 2005, 2:29:55 PM3/30/05
to Luke Palmer, Perl6 Language List
On Wed, 2005-03-30 at 13:53, Luke Palmer wrote:

> class CodeProxy {
> has Code $.code is rw;
> sub call ($a) {
> $.code($a);
> }
> }

> This is valid Perl 6, and anyone who says otherwise (because of type
> signatures) is changing the Perl philosophy too much to be taken
> seriously. Now, tell me at compile time whether the call inside `call`
> is valid.

It's valid because it's a function call on a code ref. In that case, the
compiler has to hand your request off to the run-time to check the
vtable of $.code. Clearly that's a special case. I may have missed
context at the start, but are we worried about the special cases or the
general case or the implications to the general case of the special
cases?

> At runtime, you load in a new subclass of `CodeProxy`, `DoubleCodeProxy`
> (this programmer apparently doesn't know that we support variadic
> signatures :-)

I think this is where you're stepping off the path I thought we were on,
and I agree with the rest of your message FOR ONE CASE, but not all.
Runtime loading can happen in many ways, but let's break them down into
two camps: with and without a compile-time interface definition. In the
first camp you have autoload. In the second camp you have low-level
(e.g. Parrot) code that happens to have no interface definition (because
it's pure PBC or because it came from another language) and evaluated
strings.

What I do not think should be allowed (and I may be contradicting Larry
here, which I realize is taking my life in my hands ;) is violating the
compile-time view of the "static" type tree. That is, you can load an
object "foo" at run-time, without and interface definition, but it can't
change the shape of the type tree or its existing interfaces. If it
wants to do that, it has to provide an interface def (e.g. what an
autoload module should be able to provide).

Why? Because if you can do that, then this:

my X $a;
$a.m(1) = 1;

ALWAYS has to work because you might have replaced X at run-time with a
class that has a "method m(int) is rw()", and the compiler must silently
(perhaps with an optional warning) permit it.

Please think carefully about how dynamic you want Perl 6 to be....
Dynamic is good, but there's such a thing as too much of a good thing.

Chromatic

unread,
Mar 30, 2005, 2:57:10 PM3/30/05
to Aaron Sherman, Perl6 Language List
On Wed, 2005-03-30 at 14:29 -0500, Aaron Sherman wrote:

> What I do not think should be allowed (and I may be contradicting Larry
> here, which I realize is taking my life in my hands ;) is violating the
> compile-time view of the "static" type tree. That is, you can load an
> object "foo" at run-time, without and interface definition, but it can't
> change the shape of the type tree or its existing interfaces. If it
> wants to do that, it has to provide an interface def (e.g. what an
> autoload module should be able to provide).

I disagree, *unless* you predeclare that you don't plan to change
anything -- as an optimization hint. (Though if you write a module to
do this as a policy, I won't hunt you down with my +2 club of Optimize
for the Programmer, not the Compiler.)

I certainly plan to continue to instrument code at runtime (and not just
really slushy, partially slushy, and permafrost compile time).

-- c

Larry Wall

unread,
Mar 30, 2005, 2:53:22 PM3/30/05
to Perl 6 Language
On Wed, Mar 30, 2005 at 12:05:12PM +0200, Thomas Sandlaß wrote:
: If I understand you correctly the use statement is more like a

: linker/loader directive than a compile time interface include?

That is up to the module being used. "use" is a linker, but it's
only required to link enough information into the caller such that
the caller can see the interface. Loading can happen later--though
it typically happens on first "use" rather than first use. The
point is there's no separate linker step. Everything is demand
driven, and what gets linked in at compile time depends on what gets
demanded at compile time. If something isn't demanded till run
time, the compiler can't know it, unless you tell it in advance
somehow, which presumes that *you* know it. And maybe you don't.
And that is when late binding is ever so much handier than early.

: BTW, is it foreseen to have a clear conceptual split between the


: compiler and the loader? Or is that all blurred?

Clear conceptual splits often hide false dichotomies. The split is
rather blurry for Perl 5, and Perl 6 will only continue the trend.
There are good uses for both early binding and late binding, and
for various forms of binding in between--it's a continuum, and each
module is allowed to draw the boundary wherever and however it likes.
Birds naturally prefer early binding to late binding; worms will
naturally disagree. Rolling stones gather no type constraints.

Larry

Thomas Sandlaß

unread,
Mar 30, 2005, 3:59:34 PM3/30/05
to Perl6 Language List
chromatic wrote:
> A compiler that assumes incorrectly and disallows programmers to do
> useful things because its holds those assumptions as precious is wrong
> -- especially in cases where even the programmer can't tell if code is
> valid or invalid until the program actually runs.

Me neither. One should think of a type system like dependency declarations
in a Makefile: you get out what you put in. Too few dependencies might result
in incomplete rebuilds. Complete dependencies might remind you how convoluted
your design is. And make doesn't manage circular dependencies :)
--
TSa (Thomas Sandlaß)

Chromatic

unread,
Mar 30, 2005, 3:49:09 PM3/30/05
to Aaron Sherman, Perl6 Language List
On Wed, 2005-03-30 at 15:27 -0500, Aaron Sherman wrote:

> Like I said, if you allow run-time munging of the type interfaces, then
> you can't tell if this is valid or invalid:
>
> my X $a;
> $a.m(1);
>
> you have to allow it always, regardless of the definition of X. In fact,
> you can NEVER reject ANY method or function invocation based on
> signature, since it might change at run-time.
>
> Is that really what you want?

I want the possibility to correct bad decisions of interfaces and
signatures whenever possible in the most minimally intrusive way
possible. I don't want to work around a module or class or function or
method where someone tried to be helpful by saying "Oh, this will
absolutely never change, ever" and the compiler helpfully froze that
decision forever. Sometimes that means changing it outside the
incorrect code.

I don't trust any compiler to make every decision correctly, and in
cases of ambiguity I believe that I can give it better hints than it can
reason for itself if I need the extra speed or safely.

A compiler that assumes incorrectly and disallows programmers to do
useful things because its holds those assumptions as precious is wrong
-- especially in cases where even the programmer can't tell if code is
valid or invalid until the program actually runs.

-- c

Aaron Sherman

unread,
Mar 30, 2005, 3:27:19 PM3/30/05
to chromatic, Perl6 Language List
On Wed, 2005-03-30 at 14:57, chromatic wrote:

> I certainly plan to continue to instrument code at runtime (and not just
> really slushy, partially slushy, and permafrost compile time).

That's FINE, and no one should stop you!

What I was referring to was only the items that an interface definition
would be concerned with (that is, those things which would invalidate
the compiler's assumptions).

An example follows.

Original code:

class X { method m(int $i) { say "Hello, world" } }

Valid string to eval:

q{class X { method m(int $i) { die "Goodbye, world" } }}

Invalid string to eval:

q{class X { method m(int $i) is rw($string) { say $string } }}

The latter changes the interface definition, and thus invalidates the
compiler's assumptions, but you can instrument the code all you like,
re-defining any darned thing you wish!

If you allow the interface definition to change, you're not optimizing
for the programmer. You're optimizing for the amount of time you spend
in the debugger. Failing to eval early because code would have changed
the interface (or type hierarchy) of your types will save thousands of
programmers everywhere much debugging time.

Like I said, if you allow run-time munging of the type interfaces, then
you can't tell if this is valid or invalid:

my X $a;
$a.m(1);

you have to allow it always, regardless of the definition of X. In fact,
you can NEVER reject ANY method or function invocation based on
signature, since it might change at run-time.

Is that really what you want?

--

Thomas Sandlaß

unread,
Mar 30, 2005, 3:50:39 PM3/30/05
to Perl6 Language List
Luke Palmer wrote:
> class CodeProxy {
> has Code $.code is rw;
> sub call ($a) {
> $.code($a);
> }
> }
>
> This is valid Perl 6,

Hmm, a sub in a class? I guess that should be a method. OTOH a
class is just a funny module, so might be OK. But that is the
syntax realm.


> and anyone who says otherwise (because of type
> signatures) is changing the Perl philosophy too much to be taken
> seriously. Now, tell me at compile time whether the call inside `call`
> is valid.

Ups, before we start talking past each other: I think a type system
is just a tool that reminds you to your declarations. No declarations,
no reminder, it's that simple. I would like the typing of $.code as Code
to be necessary to apply the .() invocation operator on it. The rest is
deferred to runtime if that is what you want.

And I'm completly unsure how much parrot and/or perl revert back into
compile state when you bring in new source code at runtime. Or how
much load time activity is needed for a byte code file.

The question if instances of DoubleCodeProxy are subtypes of instances
of CodeProxy boils down to the question if ($) <: ($, +$) with <: beeing
the subtype relation. Is that the right siglet syntax? Since named-only
parameters are optional the relation holds. This subtype relation allows:

my CodeProxy $cp = DoubleCodeProxy.new; # needs DoubleCodeProxy <: CodeProxy

$cp.code = sub ($x, +$y = "unknown") { say "x = $x, y = $y" };

$cp.call( "first", b => "second" ); # prints "x = first, y = second"
$cp.call( "one" ); # prints "x = one, y = unknown"

$t = $cp.code;

$cp.code = sub () { say "void" }; # no problem, type is Code
$cp.call( "X" ); # gives runtime error on closure in $.code

$cp.code = $t;

$cp.call(); # compile error: too few args
$cp.call( 1, 2 ); # compile error: too many positional args
$cp.blahh( 23 ); # compile error/warning: no such method declared

method CodeProxy::blubber(: $z ) { ... }

$cp.blubber( 42 ); # could die on ... at runtime if no .blubber autoloaded

$cp.call( 1|2|3 ); # autothreads??? I leave that to Damian :)
--
TSa (Thomas Sandlaß)

Luke Palmer

unread,
Mar 30, 2005, 4:39:49 PM3/30/05
to Thomas Sandlaß, Perl6 Language List
Thomas Sandlaß writes:

> Aaron Sherman wrote:
> >No, that was most of the point. &foo did not declare a return type, and
> >while my code was simplistic, we obviously cannot be certain what