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

A6: Complex Parameter Types

35 views
Skip to first unread message

Michael Lazzaro

unread,
Mar 10, 2003, 8:46:29 PM3/10/03
to perl6-l...@perl.org
In A6, it is confirmed that you can have "complex" types such as:

my %pet is Hash of Array of Array of Hash of Array of Cat;

It is also confirmed that you can indeed use such types in sub
signatures, e.g.:

sub foo (@a is Array of int) {...}


Confirmations/Questions:

1) Complex types for sub parameters: The above would imply that a sub
can tell the difference between an C<Array of int> vs an C<Array of
str>, thank goodness. That also implies that you can use arbitrarily
complex types, and still get the same type checking:

sub foo ( %pet is Hash of Array of Array of Hash of Array of Cat )
{...}

Yes/No?


2) Multimethod dispatch: The text would seem to _IMPLY_ that you can
perform multimethod dispatch based on complex types, but it isn't
actually stated anywhere AFAICT. e.g.

multi foo (@a is Array of str) {...}
multi foo (@a is Array of int) {...}

... is it legal, and DWYM?


3) The edge point between explicitly typed and explicitly non-typed
variables: If you pass an "untyped" array (or list?) to an explicitly
typed array parameter, is the "untyped" array considered a unique case,
or will it fail?

multi foo (@a is Array of int) {...}

my int @a = baz(); # is Array of int
my @b = baz(); # is Array of Scalar

foo(@a); # @a is typed correctly, so OK
foo(@b); # @b is not explicitly typed as C<int>; OK or FAIL?


MikeL

Damian Conway

unread,
Mar 10, 2003, 9:25:41 PM3/10/03
to perl6-l...@perl.org
Michael Lazzaro asked:


> 1) Complex types for sub parameters: The above would imply that a sub
> can tell the difference between an C<Array of int> vs an C<Array of
> str>, thank goodness. That also implies that you can use arbitrarily
> complex types, and still get the same type checking:
>
> sub foo ( %pet is Hash of Array of Array of Hash of Array of Cat ) {...}
>
> Yes/No?

Yes.


> 2) Multimethod dispatch: The text would seem to _IMPLY_ that you can
> perform multimethod dispatch based on complex types, but it isn't
> actually stated anywhere AFAICT. e.g.
>
> multi foo (@a is Array of str) {...}
> multi foo (@a is Array of int) {...}
>
> ... is it legal,

Yes.

> and DWYM?

Depends WYM. ;-)


> 3) The edge point between explicitly typed and explicitly non-typed
> variables: If you pass an "untyped" array (or list?) to an explicitly
> typed array parameter, is the "untyped" array considered a unique case,
> or will it fail?
>
> multi foo (@a is Array of int) {...}
>
> my int @a = baz(); # is Array of int
> my @b = baz(); # is Array of Scalar
>
> foo(@a); # @a is typed correctly, so OK
> foo(@b); # @b is not explicitly typed as C<int>; OK or FAIL?

Fails.

Because:

not (Array of Scalar).isa(Array of int)

Which in turn is because:

not Scalar.isa(int)

To extend the example a little further:

multi Aoi (int @a) {...}
multi AoS (Scalar @a) {...}
multi AoI (Int @a) {...}

my int @aoi;
my @aoS;
my Int @aoI;

Aoi(aoi); # Okay because int.isa(int)
Aoi(aoS); # Fails because !Scalar.isa(int)
Aoi(aoI); # Fails because !Int.isa(int)

AoS(aoi); # Fails because !int.isa(Scalar)
AoS(aoS); # Okay because Scalar.isa(Scalar)
AoS(aoI); # Okay because Int.isa(Scalar)

AoI(aoi); # Fails because !int.isa(Int)
AoI(aoS); # Fails because !Scalar.isa(Int)
AoI(aoI); # Okay because Int.isa(Int)


Damian

Larry Wall

unread,
Mar 11, 2003, 12:32:53 AM3/11/03
to perl6-l...@perl.org
On Tue, Mar 11, 2003 at 01:25:41PM +1100, Damian Conway wrote:
: >3) The edge point between explicitly typed and explicitly non-typed
: >variables: If you pass an "untyped" array (or list?) to an explicitly
: >typed array parameter, is the "untyped" array considered a unique case,
: >or will it fail?
: >
: > multi foo (@a is Array of int) {...}
: >
: > my int @a = baz(); # is Array of int
: > my @b = baz(); # is Array of Scalar
: >
: > foo(@a); # @a is typed correctly, so OK
: > foo(@b); # @b is not explicitly typed as C<int>; OK or FAIL?
:
: Fails.
:
: Because:
:
: not (Array of Scalar).isa(Array of int)
:
: Which in turn is because:
:
: not Scalar.isa(int)

I dunno. I can argue that it should coerce that. It'll certainly be
able to coerce a random scalar to int for you, so it's not a big stretch
to coerce conformant arrays of them. On the other hand, it's likely
to be expensive in some cases, which isn't so much of an issue for
single scalars/ints/strs.

Larry

Dave Whipp

unread,
Mar 11, 2003, 12:37:55 AM3/11/03
to perl6-l...@perl.org
Larry Wall wrote:

> I dunno. I can argue that it should coerce that. It'll certainly be
> able to coerce a random scalar to int for you, so it's not a big stretch
> to coerce conformant arrays of them. On the other hand, it's likely
> to be expensive in some cases, which isn't so much of an issue for
> single scalars/ints/strs.

If the coercion is lazy, then its not really any more expensive than
coercing individual scalars, as needed.


Dave.

Damian Conway

unread,
Mar 11, 2003, 1:12:54 AM3/11/03
to Larry Wall, perl6-l...@perl.org
Larry wrote:

> : > multi foo (@a is Array of int) {...}
> : >
> : > my int @a = baz(); # is Array of int
> : > my @b = baz(); # is Array of Scalar
> : >
> : > foo(@a); # @a is typed correctly, so OK
> : > foo(@b); # @b is not explicitly typed as C<int>; OK or FAIL?
> :
> : Fails.
> :
> : Because:
> :
> : not (Array of Scalar).isa(Array of int)
> :
> : Which in turn is because:
> :
> : not Scalar.isa(int)
>
> I dunno. I can argue that it should coerce that. It'll certainly be
> able to coerce a random scalar to int for you, so it's not a big stretch
> to coerce conformant arrays of them. On the other hand, it's likely
> to be expensive in some cases, which isn't so much of an issue for
> single scalars/ints/strs.

Hmmmm. I guess it depends whether we treat type specifications as constraining
the actual arguments, or restricting the interface of the formal parameters.

That is, does:

multi foo (@a is Array of int) {...}

mean:
"Don't let them pass anything but an
Array of int-or-subclass-of-int"

or does it mean:

"Let them pass Array of (anything)
and then treat the anythings as ints"

For my money, the former (i.e. compile-time type strictness) makes more sense
under pass-by-reference semantics, whereas the latter (i.e. run-time type
coercion) is more appropriate under pass-by-copy.


Damian

Austin Hastings

unread,
Mar 11, 2003, 10:41:41 AM3/11/03
to perl6-l...@perl.org
Oh goody, two with one blow:

--- Damian Conway <dam...@conway.org> wrote:
> Larry wrote:
>
> > : > multi foo (@a is Array of int) {...}
> > : >
> > : > my int @a = baz(); # is Array of int
> > : > my @b = baz(); # is Array of Scalar
> > : >
> > : > foo(@a); # @a is typed correctly, so OK
> > : > foo(@b); # @b is not explicitly typed as C<int>; OK or
> FAIL?
> > :
> > : Fails.
> > :
> > : Because:
> > :
> > : not (Array of Scalar).isa(Array of int)
> > :
> > : Which in turn is because:
> > :
> > : not Scalar.isa(int)
> >
> > I dunno. I can argue that it should coerce that. It'll certainly
> be
> > able to coerce a random scalar to int for you, so it's not a big
> stretch
> > to coerce conformant arrays of them. On the other hand, it's
> likely
> > to be expensive in some cases, which isn't so much of an issue for
> > single scalars/ints/strs.

I saw a description in A6 saying that an Array of Foo was basically a
function which mapped int to Foo.

That being the case, a coercive @scalars -> @ints "cast" used to
translate an array parameter would really be a "wrapped" (function
mapping ints to scalars) whose wrapper converted the returned result to
ints.

Is there going to be some generic way to do this?

That is: Can I say

SomeType @a; # extern, anyone?

sub SomeType2int(int)
returns int
{
my Some $some = call;
return 0 + $some;
}

@a.wrap(SomeType2int);

And having said that, then call:

sub b(@input is Array of int) {...}

b(@a);

>
> Hmmmm. I guess it depends whether we treat type specifications as
> constraining the actual arguments, or restricting the interface of
> the formal parameters.
>
> That is, does:
>
> multi foo (@a is Array of int) {...}
>
> mean:
> "Don't let them pass anything but an
> Array of int-or-subclass-of-int"
>
> or does it mean:
>
> "Let them pass Array of (anything)
> and then treat the anythings as ints"
>
> For my money, the former (i.e. compile-time type strictness) makes
> more sense
> under pass-by-reference semantics, whereas the latter (i.e. run-time
> type
> coercion) is more appropriate under pass-by-copy.

You're treading dangerously close to the S&M line there...

Does it make sense to say C<use strict params;> for this stuff?

=Austin

Michael Lazzaro

unread,
Mar 11, 2003, 1:15:35 PM3/11/03
to perl6-l...@perl.org
Larry wrote:
>> : > multi foo (@a is Array of int) {...}
>> : >
>> : > my int @a = baz(); # is Array of int
>> : > my @b = baz(); # is Array of Scalar
>> : >
>> : > foo(@a); # @a is typed correctly, so OK
>> : > foo(@b); # @b is not explicitly typed as C<int>; OK or FAIL?
<snip>

>> I dunno. I can argue that it should coerce that.

Damian wrote:
> Hmmmm.

I think that IF there's a significant speed issue there at all, it
should just fail. (IMO, it's more important to be able to optimize for
speed.) But the issue is of course that when people make temporary
arrays, I imagine they'll seldom be explicitly typed, because we all
like to be lazy slobs:

sub baz returns Array of int {...}

my @temp = baz();

Since this is such a common case, it might be possible to leave a lazy
'hint' after the assignment such that @temp is treated as an C<Array of
int>, because we know for a fact that's how it was (last) assigned.
The next operation on @temp would clear the hint:

push @temp, $i; # well, now we don't know *what* type @temp
returns


(I hate to niggle at all over such a stupid little edge case, but I
worry it's going to be terribly common.)

MikeL

P.S. I'm so happy I'm getting my multimethod-friendly Array of Array of
Cats! Herding Cats is so much easier when they're placed in explicitly
typed multidimensional buckets!

Damian Conway

unread,
Mar 11, 2003, 10:06:39 PM3/11/03
to perl6-l...@perl.org
Austin Hastings wrote:

> You're treading dangerously close to the S&M line there...

Sure. That's exactly what types are for.


> Does it make sense to say C<use strict params;> for this stuff?

I'd much rather that simply using typed params invoked type stricture.

Damian

Austin Hastings

unread,
Mar 12, 2003, 11:06:06 AM3/12/03
to Damian Conway, perl6-l...@perl.org

--- Damian Conway <dam...@conway.org> wrote:
> Austin Hastings wrote:
>
> > You're treading dangerously close to the S&M line there...
>
> Sure. That's exactly what types are for.
>

Granted.

> > Does it make sense to say C<use strict params;> for this stuff?
>
> I'd much rather that simply using typed params invoked type
> stricture.

Sure, but there's a difference between "C" (Boy Scout Oath) strictness
and "C++" (FBI will visit your home and do a thorough background check
before we proceed) strictness.

In this case, I rather like the idea of being able to say

sub foo(@a is Array of Int) {...}

my @input = read_a_bunch_o_data();
foo(@input);


Where the compiler will automatically "wrap" the @input array in a
make-it-an-int converter. This, to me, is DWIM.

Connecting this with some thinking/talking about wrappage, it occurs to
me that:

C<call> is just a keyword. Which means that instead of doing

foo.wrap({ ...; call; ... });

I could just as easily do:

sub wrapper(...) { ...; call ; ... }
foo.wrap(&wrapper);

Right?

I think there may be a library of wrapper "helper-functions" used by
both the compiler and module-writers. Things that do coercive behavior
rather than try-but-maybe-fail behavior.

So the "use strict signatures;" wouldn't be a switch invoking type
stricture, per se. What it would do is convert from "DWIM stricture" to
"Patriot Act stricture".

=Austin

Damian Conway

unread,
Mar 12, 2003, 2:07:56 PM3/12/03
to perl6-l...@perl.org
Austin Hastings wrote:

> In this case, I rather like the idea of being able to say
>
> sub foo(@a is Array of Int) {...}
>
> my @input = read_a_bunch_o_data();
> foo(@input);
>
>
> Where the compiler will automatically "wrap" the @input array in a
> make-it-an-int converter. This, to me, is DWIM.

But to many others it's not DWIS ("Do What I Said"). To them, types are about
compile-time checking of constraints on the run-time compatibility of data.
So they would argue that declaring C<foo> like that implies that any argument
passed to C<foo> ought to guarantee that it already contains Ints, rather than
specifying a (possibly unsuccessful) run-time coercion to ensure that
condition. And many would argue that implicit coercions on typed parameters is
one of the major *problems* with C++.

My own problem with this wrapping notion is that I consider it incompatible
with the reference semantics of normal Perl 6 parameters. Specifically, I
*don't* want the aliasing mechanism to be capable of implicit type coercions.

On the other hand, I have *no* problem with this:

sub foo(@a is Array of Int is copy) {...}
^^^^^^^

doing the kind of coercive wrapping you're suggesting.


> Connecting this with some thinking/talking about wrappage, it occurs to
> me that:
>
> C<call> is just a keyword. Which means that instead of doing
>
> foo.wrap({ ...; call; ... });

That has to be:

&foo.wrap({ ...; call; ... });


> I could just as easily do:
>
> sub wrapper(...) { ...; call ; ... }
> foo.wrap(&wrapper);
>
> Right?

&foo.wrap(&wrapper);

But, yes.


> I think there may be a library of wrapper "helper-functions" used by
> both the compiler and module-writers. Things that do coercive behavior
> rather than try-but-maybe-fail behavior.

Quite possibly. Though wrapping is probably too "heavy" a mechanism for type
coercions. When I imagine type coercions in Perl 6, I imagine them as
compile-time detectable, and explicit.


> So the "use strict signatures;" wouldn't be a switch invoking type
> stricture, per se. What it would do is convert from "DWIM stricture" to
> "Patriot Act stricture".

I still believe that the default level of type-checking you're proposing is
the wrong way round.

Damian


Austin Hastings

unread,
Mar 12, 2003, 3:29:50 PM3/12/03
to Damian Conway, perl6-l...@perl.org

--- Damian Conway <dam...@conway.org> wrote:
> Austin Hastings wrote:
>
> > In this case, I rather like the idea of being able to say
> >
> > sub foo(@a is Array of Int) {...}
> >
> > my @input = read_a_bunch_o_data();
> > foo(@input);
> >
> >
> > Where the compiler will automatically "wrap" the @input array in a
> > make-it-an-int converter. This, to me, is DWIM.
>
> But to many others it's not DWIS ("Do What I Said"). To them, types
> are about compile-time checking of constraints on the run-time
> compatibility of data.

No problem so far. And when they run -w, perl should tell them
"subroutine call not provably (in)correct at line 3"

> So they would argue that declaring C<foo> like that implies that any
> argument passed to C<foo> ought to guarantee that it already
> contains Ints, rather than specifying a (possibly unsuccessful)
> run-time coercion to ensure that condition.

But what's the vision for p6?

My expectation is that the type-checking stuff will be heavily used
for:

1- Large scale projects.

2- CPAN modules.

I expect that the folks who want to do one-liners will still want to be
able to say C<perl6 -MCPAN whatever>

So the "strict" bits have to be forgiving of the "non-strict" callers.

To me, the way around this is NOT to force interface contracts down to
some lowest common denominator, but rather to document what is
expected, and then allow the developer to accept responsibility for
stepping on his crank.

If you say, "Give me an Array of Hash of (String => Array of BigNum)"
and I say, "Here's an Array of Scalars. They're OK, I promise" and
things "gang agley", it's my fault. I can deal with that.

And if you have said "Array of Hash of (String => Array of BigNum)"
then I can theoretically give you a typed array and let the compiler
check it.

However, if you have been forced to say "Give me an Array" in the
interests of avoiding typewhacking one-liners, then (1) you've got to
check and throw the exception, slowing up your code; and (2) there's no
option for me if I *DO* want to conform to the rigid rules.


> And many would argue that implicit coercions on typed
> parameters is one of the major *problems* with C++.

Of course it is. And why is that? Because they've GOT strongly typed
parameters, but sometimes the data doesn't match the signature. This
has been exacerbated by differentiating const from non-const types, and
by that foul fire-hose of code known as the template mechanism.

But at the bottom, the C++ problem is a problem of its own making --
people want coercion. Just like me. I want coercion. I want the ability
to take advantage of the really nifty, carefully written code available
from dedicated, rigorous hackers like this Conway fellow. But I want to
do it as a slap-dash hack with no thought given to type stuff. How?

>
> My own problem with this wrapping notion is that I consider it
> incompatible with the reference semantics of normal Perl 6
> parameters. Specifically, I *don't* want the aliasing mechanism to
> be capable of implicit type coercions.

Maybe you could expand on that a little?

> On the other hand, I have *no* problem with this:
>
> sub foo(@a is Array of Int is copy) {...}
> ^^^^^^^
> doing the kind of coercive wrapping you're suggesting.

What's the difference? (Aside from the obvious, that is...)


> > I think there may be a library of wrapper "helper-functions" used
> by
> > both the compiler and module-writers. Things that do coercive
> behavior
> > rather than try-but-maybe-fail behavior.
>
> Quite possibly. Though wrapping is probably too "heavy" a mechanism
> for type
> coercions. When I imagine type coercions in Perl 6, I imagine them as
> compile-time detectable, and explicit.

Ahh, but if you don't want heavy, just comply with the expected type,
or do your own coercion. I certainly imagine them as compile-time
detectable (I don't want all subcalls to be wrapped), but obvious,
implicit coercions should get a configurable warning and a wrapper.

sub foo(@a of Scalar) {...}

foo(1, 2, 3); # OKAY

foo(@a, @b, @c); # WARNING: foo(List of Array) will be coerced to
foo(Array of Scalar) using &foo.wrap(__flattenArrays)

foo(**(@a, @b, @c)); # OKAY: Fixed it using SuperMario operator.


> > So the "use strict signatures;" wouldn't be a switch invoking type
> > stricture, per se. What it would do is convert from "DWIM
> > stricture" to "Patriot Act stricture".
>
> I still believe that the default level of type-checking you're
> proposing is the wrong way round.

Hmm. Okay. I'll let you reverse the default levels if you give me a
single-letter command line switch to go back to "slouch-mode"..

=Austin

Michael Lazzaro

unread,
Mar 12, 2003, 3:56:54 PM3/12/03
to perl6-l...@perl.org

On Wednesday, March 12, 2003, at 11:07 AM, Damian Conway wrote:
> Austin Hastings wrote:
>
>> In this case, I rather like the idea of being able to say
>> sub foo(@a is Array of Int) {...}
>> my @input = read_a_bunch_o_data();
>> foo(@input);
>> Where the compiler will automatically "wrap" the @input array in a
>> make-it-an-int converter. This, to me, is DWIM.
>
> But to many others it's not DWIS ("Do What I Said"). To them, types
> are about compile-time checking of constraints on the run-time
> compatibility of data.
> So they would argue that declaring C<foo> like that implies that any
> argument passed to C<foo> ought to guarantee that it already contains
> Ints, rather than specifying a (possibly unsuccessful) run-time
> coercion to ensure that condition. And many would argue that implicit
> coercions on typed parameters is one of the major *problems* with C++.

Agreed. It should do compile-time verification, not runtime.

That said, I still think there *might* be something to be said for
compile-time 'hints' for explicitly _untyped_ values. Take the example:

sub foo(str $s) {...}

my str $a = 'blah'; # type 'str'
my $b = 'blah'; # untyped, but set to a constant 'str'
my $c = $a; # untyped, but set to a known typed 'str'
my $d = $a + $b; # untyped, but known to be of type 'str'

foo($a); # OK
foo($b); # COMPILE ERROR
foo($c); # COMPILE ERROR
foo($d); # COMPILE ERROR

With strict typing, the last three lines are errors. But it is known
that $b,$c,$d were all set to known C<str> values and have not since
been altered. So the compiler *could* infer the type of these
variables without them explicitly being stated, we *might* choose to
catch those cases and make them non-errors. (You can actually track
the 'type' hints quite a ways, if the operations you're doing produced
typed values.)

That would allow the use of quickie untyped temporary variables. For
example, if you have a little ten-line script that uses type-aware
CP6AN subs, but yer a lazy slob.

Again, POMTC (percentage of me that cares), 50%. Not a showstopper.
Just an idea.

MikeL

Michael Lazzaro

unread,
Mar 12, 2003, 4:40:34 PM3/12/03
to perl6-l...@perl.org
I think the issue of type coercion (forcing one type to another) should
be decided separately from the issue of "implicit" types (recognizing
when an untyped variable can be KNOWN at a given point to hold a
specific type, even if it isn't explicitly stated.)

As far as true coercion goes: for the sake of example, let's assume we
have a class, C<MyClass>, that doesn't inherit from str, but that can
be converted to a str if necessary.

sub foo(str $s);

my str $a = 'blah';

my MyClass $myclass = MyClass.new;

foo($a); # OK
foo($myclass); # COMPILE ERROR; WRONG TYPE

That last line is a type coercion... we know it's one type, but it
needs to be another. Previous implication is that you'd do it like
this:

foo($myclass.str); # OK

I'm not keen on that syntax, personally, because it means you're
cluttering up the method namespace of MyClass with every possible
type/class you want to be able to convert to, which I have to think
would cause problems in larger libraries. I at one point suggested:

class MyClass {
to str {...}
to num {...} # etc
}

foo($myclass to str);

as a way to separate the 'coercions' namespace from the normal
'methods' namespace. But regardless, the A6 question being asked is
whether the C<sub> or C<multi> can know when it's allowed to implicitly
coerce arguments from one type to another, such that

foo($myclass);

can work without having to first define 'foo' variants to explicitly
handle each and every possible type. Possibilities:

(1) If MyClass isa str, or MyClass implements str, problem solved -- it
will work on anything that a str works on, right? (But maybe you don't
want your class to actually _implement_ str, because we don't
ordinarily want it to be considered a str... we actually just want a
one-directional way to _convert_ instances to strs.)

(2) Maybe we can mark a given C<multi> as being specifically willing to
coerce an argument if necessary, probably with a trait:

multi foo (str $s) {...} # Normal syntax
multi foo ($s to str) {...} # OK, should attempt to COERCE $s to
a str
multi foo (str from $s) {...} # alternate spelling???

... thereby identifying that we want to convert it if the compiler
and/or runtime can figure out how. Would something like that achieve
the desired effect?

My main points being, untyped-to-typed coercion is different from true
type coercion, and that the 'coerce to' and 'implements' relationships
might be worthy of syntactic separation.

POMTC: 70%

MikeL

Luke Palmer

unread,
Mar 12, 2003, 4:50:34 PM3/12/03
to mlaz...@cognitivity.com, perl6-l...@perl.org
> Agreed. It should do compile-time verification, not runtime.
>
> That said, I still think there *might* be something to be said for
> compile-time 'hints' for explicitly _untyped_ values.

Perhaps there should be a distinction between "untyped" and "Object".
Something that is explicitly an Object must be dynamically cast to
conform to a narrower type (however dynamic casting actually works).
If a variable is untyped, i.e. not explicitly anything at all, it
could be subject to run-time automatic coercion (of course resulting
in an exception if they are incompatible). That would allow us
lazy folks to still get what we want with weak typing.

> Take the example:
>
> sub foo(str $s) {...}
>
> my str $a = 'blah'; # type 'str'
> my $b = 'blah'; # untyped, but set to a constant 'str'
> my $c = $a; # untyped, but set to a known typed 'str'
> my $d = $a + $b; # untyped, but known to be of type 'str'
>
> foo($a); # OK
> foo($b); # COMPILE ERROR
> foo($c); # COMPILE ERROR
> foo($d); # COMPILE ERROR

Requiring this kind of behavior (from a "standard writer" point of
view) wouldn't be such a good idea, though. You could say it's up to
the compiler to do whatever sort of data flow analysis it wants, and
may catch these things at compile time if it can. Consider this:

sub baz(Int $x) {...}

my $read = 0;
my $foo = 'Glippy glop gloopy';
for 1..10 {
$foo = <> if $read;
}
baz $foo; # ERROR: type incompatibility.

A smart compiler could catch that one, but a dumb one should be
allowed not to and defer it until runtime. But---it's probably the
case that there will still only be one Perl, so it's not really an
issue. Except for the doc folks.

Naturally, in a threaded environment, declaring something as C<is
shared> would be much like C<volatile> in C.

Luke

Leopold Toetsch

unread,
Mar 12, 2003, 6:05:11 PM3/12/03
to Austin_...@yahoo.com, Damian Conway, perl6-l...@perl.org
Austin Hastings wrote:

>
> But what's the vision for p6?
>
> My expectation is that the type-checking stuff will be heavily used
> for:
>
> 1- Large scale projects.
>
> 2- CPAN modules.


3- speed

When you are not on perl one liners, but maybe some inner tight loops of
some algorithm or whatever, where speed does matter, we (the low level
PASM folks :) have achieved a speed increase of x100 and more, on a now
of course hand crafted assembler routine compared to $HLInterpLang.

These are the margins that a good optimizer will be able to hopefully
reach, which are for plain integers near or beyond gcc -O3 performance
with the JIT runtime.
When the flexibility of all the lanugage comes in, this is of course not
possible, and the more of those features (caller, %MY, leave,
exceptions) are used, the less, optimizations can be used. But for "what
is the type-checking stuff": for sure for better diagnostic messages and
for performance, when needed.

> =Austin


leo


Damian Conway

unread,
Mar 13, 2003, 6:21:25 AM3/13/03
to perl6-l...@perl.org
Austin Hastings wrote:

> But what's the vision for p6?
>
> My expectation is that the type-checking stuff will be heavily used
> for:
>
> 1- Large scale projects.
>
> 2- CPAN modules.
>
> I expect that the folks who want to do one-liners will still want to be
> able to say C<perl6 -MCPAN whatever>
>
> So the "strict" bits have to be forgiving of the "non-strict" callers.

To me that's a contradiction in terms. Strictness can't be "forgiving".
At the risk of stating the obvious, that's the whole point of strictness:
to be strict.

What we're suggesting is that if you want to be non-strict, don't use types.
After all, they're optional.

"But," (I hear you say), "I may not have a choice! If the Cruel Module Author
chose to use types, then I'm forced to do so too. That's not Lazy."

And you're right. The module author gets to determine the interface of their
module. And if they determine that that interface will provide type-safety,
then you have to respect that. Just as if they decided that it would have an
OO interface, or would set PRE and POST conditions on calls, or would only
take named arguments.

Because the author of the module is *relying* on that interface to ensure the
correctness of their code; to prevent inappropriate data being passed to their
subroutines. Typing is yet another way in which they can relax, knowing that
the wrong type of data will be headed off at the (compiler) pass.


> To me, the way around this is NOT to force interface contracts down to
> some lowest common denominator, but rather to document what is
> expected, and then allow the developer to accept responsibility for
> stepping on his crank.

Yep. And the way for the client of some module to accept that responsibility
is to put in an explicit conversion.


> If you say, "Give me an Array of Hash of (String => Array of BigNum)"
> and I say, "Here's an Array of Scalars. They're OK, I promise" and
> things "gang agley", it's my fault. I can deal with that.

But large projects -- where typing will be most important -- *can't* deal with
that. That's the point of typing: to specify and enforce interface contracts.
At compile-time if at all possible.


> But at the bottom, the C++ problem is a problem of its own making --
> people want coercion. Just like me. I want coercion. I want the ability
> to take advantage of the really nifty, carefully written code available
> from dedicated, rigorous hackers like this Conway fellow. But I want to
> do it as a slap-dash hack with no thought given to type stuff. How?

By distinguishing a parameter that *requires* a particular type, from a
parameter that *ensures* a particular type (by coercion if necessary). I've
suggested that using C<is copy> semantics should indicate "make whatever I'm
actually given into one of these, if you can", whereas reference semantics
say "this must already be one of these" (by their very nature, since an
aliased parameter *is* the argument).

Alternatively, one might imagine a separate and explicit trait that specifies
that a parameter is allowed to coerce its corresponding argument to it's own type.

But what we can't allow is a reference parameter to coerce its argument. For
example:

sub loggedincr (Int $i is rw) {
print $log: "Incremented $i.id()\n";
$i++ unless $i.constant;
}

# and later...

int $x = 7;
loggedincr($x);

The $i parameter *can't* be bound to $x, because $x doesn't have the necessary
features to be an Int (e.g. it doesn't have the capacity to respond to method
or property lookups). Even wrapping $x in a temporary Int wouldn't help, since
it would render the logging of an .id meaningless. The whole point of having
types is to catch this kind of problem at compile-time.


>>My own problem with this wrapping notion is that I consider it
>>incompatible with the reference semantics of normal Perl 6
>>parameters. Specifically, I *don't* want the aliasing mechanism to
>>be capable of implicit type coercions.
>
> Maybe you could expand on that a little?

See above.


>>On the other hand, I have *no* problem with this:
>>
>> sub foo(@a is Array of Int is copy) {...}
>> ^^^^^^^
>>doing the kind of coercive wrapping you're suggesting.
>
>
> What's the difference? (Aside from the obvious, that is...)

Suppose I call:

foo(@x);

The difference is that @a above is a lexically scoped variable into
which the contents of the corresponding argument (@x) are copied (i.e.
assigned). Now it's perfectly possible that the contents of @x can be assigned
into @a, even if the argument and the parameter are of wildly different types.

But if I say:

sub foo(@a is Array of Int) {...}

...
foo(@x);

then I'm saying: "within &foo, @a is just another name for @x". So they are
(temporarily) the same thing. That can only (be allowed to) happen if the
actual type of @x satisfies all the requirements of the declared type of @a.


Damian

Piers Cawley

unread,
Mar 13, 2003, 7:43:46 AM3/13/03
to Damian Conway, perl6-l...@perl.org
Damian Conway <dam...@conway.org> writes:

Of course, a module author could always layer on a 'non strict'
version of the interface that did the type conversions itself before
passing them into the more stringent internals.

>> But at the bottom, the C++ problem is a problem of its own making --
>> people want coercion. Just like me. I want coercion. I want the ability
>> to take advantage of the really nifty, carefully written code available
>> from dedicated, rigorous hackers like this Conway fellow. But I want to
>> do it as a slap-dash hack with no thought given to type stuff. How?
>
> By distinguishing a parameter that *requires* a particular type, from
> a parameter that *ensures* a particular type (by coercion if
> necessary). I've suggested that using C<is copy> semantics should
> indicate "make whatever I'm actually given into one of these, if you
> can", whereas reference semantics say "this must already be one of
> these" (by their very nature, since an aliased parameter *is* the
> argument).
>
> Alternatively, one might imagine a separate and explicit trait that
> specifies that a parameter is allowed to coerce its corresponding
> argument to it's own type.

But this would be better. And implementable relatively easily as a
macro if it weren't availabe in the core I would have
thought. Hm... it might be interesting/useful to implement a set of
macros to allow developers to define and export their own traits:

trait foo (...) is ParameterTrait {
COMPILATION_CHECK {...}
RUNTIME_CHECK {...}
}

(There's almost certainly a better syntax available mind).


--
Piers

Larry Wall

unread,
Mar 13, 2003, 12:24:06 PM3/13/03
to perl6-l...@perl.org
On Thu, Mar 13, 2003 at 10:21:25PM +1100, Damian Conway wrote:
: But what we can't allow is a reference parameter to coerce its argument.
: For example:
:
: sub loggedincr (Int $i is rw) {
: print $log: "Incremented $i.id()\n";
: $i++ unless $i.constant;
: }
:
: # and later...
:
: int $x = 7;
: loggedincr($x);
:
: The $i parameter *can't* be bound to $x, because $x doesn't have the
: necessary features to be an Int (e.g. it doesn't have the capacity to
: respond to method or property lookups).

But we've already said that an int will try to behave like an Int
as much as it can, including calling methods on it as if it were
an Int. The "int" is just a hint that you'd like compact storage
and fast loops And it's probably a stronger hint on an array than
on a solitary scalar.

: Even wrapping $x in a temporary Int

: wouldn't help, since it would render the logging of an .id meaningless. The
: whole point of having types is to catch this kind of problem at
: compile-time.

A proxy Int could certainly calculate its id from the location of the
actual int, if it came down to it, so that different proxy Ints have
the same id. And if the outer scope establishes the proxy Int at the
same time as it allocates the int, there will only be one of it anyway.

And we'd only need to create the proxy if we know we might be passing it
to an C<rw> parameter somewhere. C<constant> parameters on Int, Num, Str
and Ref should be free to implement themselves with copy semantics anyway.
If you want to take a ref to a parameter, you'll have to declare it C<ref>
or C<rw>, I suspect.

Things get dicier with viewing an Array of int as an Array of Int, but
again, the Array of Int could know that it's a proxy for an Array of int.
It's just an object with an interface, after all. And it could generate
smart proxy references as the need arises, in cooperation with the Array
of int as necessary to avoid .id bifurcation.

I am not suggesting that we open up the barn doors to allow anything
to be coerced to anything else willy nilly. I am suggesting that we
maintain the easy integer <-> number <-> string semantics of Perl 5
when the rest of the type system allows it. Data structures would
still have to be conformant--I'm talking about the leaf semantics,
not the tree semantics.

Larry

Angel Faus

unread,
Mar 13, 2003, 12:28:00 PM3/13/03
to Damian Conway, perl6-l...@perl.org

Damian Conway wrote:
> But large projects -- where typing will be most important --
> *can't* deal with that. That's the point of typing: to specify and
> enforce interface contracts. At compile-time if at all possible.

One quick question about this. If I write:

sub foo (Bar $f) {..}

my $x = some_function_that_returns_bar();
foo($x);

Will this fail because the compiler _requires_ that the type be known
at compile-time, or will it work because it will the delay the
type-checking until run-time?

-angel

Larry Wall

unread,
Mar 13, 2003, 11:56:18 AM3/13/03
to perl6-l...@perl.org
On Thu, Mar 13, 2003 at 10:21:25PM +1100, Damian Conway wrote:
: But if I say:

:
: sub foo(@a is Array of Int) {...}
: ...
: foo(@x);
:
: then I'm saying: "within &foo, @a is just another name for @x". So they are
: (temporarily) the same thing. That can only (be allowed to) happen if the
: actual type of @x satisfies all the requirements of the declared type of @a.

Which condition holds if @x is an array of Scalar! Because a Scalar
polymorphically supports the Int, Num, Str, and Ref interfaces.

Larry

Larry Wall

unread,
Mar 13, 2003, 1:19:55 PM3/13/03
to perl6-l...@perl.org
On Thu, Mar 13, 2003 at 06:28:00PM +0100, Angel Faus wrote:
:

In the absence of a pragma to the contrary, type checking will be done
at run time when the type can't be determined at compile time. But
surely the S&M&B&D types will create a pragma that requires the type
to be known at compile time.

larry

Austin Hastings

unread,
Mar 13, 2003, 2:31:30 PM3/13/03
to Damian Conway, perl6-l...@perl.org

--- Damian Conway <dam...@conway.org> wrote:
> Austin Hastings wrote:
>
> > But what's the vision for p6?
> >
> > My expectation is that the type-checking stuff will be heavily used
> > for:
> >
> > 1- Large scale projects.
> >
> > 2- CPAN modules.
> >
> > I expect that the folks who want to do one-liners will still want
> to be
> > able to say C<perl6 -MCPAN whatever>
> >
> > So the "strict" bits have to be forgiving of the "non-strict"
> callers.
>
> To me that's a contradiction in terms. Strictness can't be
> "forgiving". At the risk of stating the obvious, that's the
> whole point of strictness: to be strict.

Of course strictness can be forgiving. That's why the "Insert Coin"
slots on soda machines have a beveled slot -- they expect the user to
be a little uncoordinated, but once inside it's going to be all
by-the-book.


>
> What we're suggesting is that if you want to be non-strict, don't use
> types. After all, they're optional.
>
> "But," (I hear you say), "I may not have a choice! If the Cruel
> Module Author chose to use types, then I'm forced to do so too.
> That's not Lazy."
>
> And you're right. The module author gets to determine the interface
> of their module. And if they determine that that interface will
> provide type-safety, then you have to respect that. Just as if
> they decided that it would have an OO interface, or would set
> PRE and POST conditions on calls, or would only take named
> arguments.
>
> Because the author of the module is *relying* on that interface to
> ensure the correctness of their code; to prevent inappropriate data
> being passed to their subroutines. Typing is yet another way in
> which they can relax, knowing that the wrong type of data will be
> headed off at the (compiler) pass.
>

But this isn't really true, is it? When you come down to cases, you can
construct a case wherein the wrong type of data gets coerced into the
"round hole" in every language.

sub foo($a is Int);

my $line = "Hello, World";
my Int $i ::= $line;

foo($i); # Compiles okay, doesn't work.

So the module author is relying on the module user to make sure that
the intent of the interface is satisfied. The type bits are there to
help them communicate, and the let the compiler jab the willing user in
the ribs when he goofs.

> > To me, the way around this is NOT to force interface contracts down
> to
> > some lowest common denominator, but rather to document what is
> > expected, and then allow the developer to accept responsibility for
> > stepping on his crank.
>
> Yep. And the way for the client of some module to accept that
> responsibility
> is to put in an explicit conversion.

This statement gives me the heebie-jeebies.

Perl5 ==================================[*]===== Java/C

extern long factorial(int);
char buf [BUFMAX];

while (gets(buf))
{
if (want_fact())
{
int i;

i = atoi(buf);
printf("%ld", factorial(i));
}
}

Versus:

use Math qw(factorial(Int $));
my $line;

while ($line = <>)
{
if (want_fact())
{
my Int $i = $line; # Or do I need C<my Int $i = $line.Int;> ?
print factorial($i);
}
}


Say it ain't so, Joe!


> > If you say, "Give me an Array of Hash of (String => Array of
> BigNum)"
> > and I say, "Here's an Array of Scalars. They're OK, I promise" and
> > things "gang agley", it's my fault. I can deal with that.
>
> But large projects -- where typing will be most important -- *can't*
> deal with that. That's the point of typing: to specify and enforce
> interface contracts. At compile-time if at all possible.


The large project folks will have the standard C<use ludicrous
typesafety;> at the top of their files, so no problem.


> > But at the bottom, the C++ problem is a problem of its own making
> > -- people want coercion. Just like me. I want coercion. I want
> > the ability to take advantage of the really nifty, carefully
> > written code available from dedicated, rigorous hackers like
> > this Conway fellow. But I want to do it as a slap-dash hack
> > with no thought given to type stuff. How?

> By distinguishing a parameter that *requires* a particular type,
> from a parameter that *ensures* a particular type (by coercion
> if necessary). I've suggested that using C<is copy> semantics
> should indicate "make whatever I'm actually given into one of
> these, if you can", whereas reference semantics say "this must
> already be one of these" (by their very nature, since an
> aliased parameter *is* the argument).

Hmm. Granted that references must be basically correct when you use
them, there's really two cases here:

1- I give you something that's just wrong, but convertible. Example:
Array of int/Int.

In this case, I'm not sure that

2- I give you something that I know is right, but I don't have all the
type fluff attached. Example: Int/Scalar.

In this case, the data is essentially right, and I need a way to tell
the typechecker: Hey, I'm right! Shaddap.

Consider the case of an XML datastore:

<struct>
<list name="a"><item value=1/><item value=2/><item value=3/></list>
<list name="b"><item value=4/><item value=5/><item value=6/></list>
<list name="c"><item value=7/><item value=8/><item value=9/></list>
</struct>

When I C<use XML::P6Parser>, it's going to give me a {Hash of String =>
Array of Scalar} or maybe {Array of {String, Array of Scalar}}

When I C<use Statistics::AnalyzeThis;>, it's going to want an Array of
Ints or Nums or some such. So while I'm willing to say:

analyze(%hoAoS.values);

I'm leery of the complex-cast semantics. (Speaking of which, what are
they?)

analyze(%hoAoS.values.dynamic_cast(Array of Int)); # Shades of C++


> Alternatively, one might imagine a separate and explicit trait that
> specifies that a parameter is allowed to coerce its corresponding
> argument to it's own type.
>
> But what we can't allow is a reference parameter to coerce its
> argument. For example:
>
> sub loggedincr (Int $i is rw) {
> print $log: "Incremented $i.id()\n";
> $i++ unless $i.constant;
> }
>
> # and later...
>
> int $x = 7;
> loggedincr($x);
>
> The $i parameter *can't* be bound to $x, because $x doesn't have the
> necessary features to be an Int (e.g. it doesn't have the capacity
> to respond to method or property lookups). Even wrapping $x in a
> temporary Int wouldn't help, since it would render the logging of
> an .id meaningless. The whole point of having types is to catch
> this kind of problem at compile-time.

Yep. Typesafe compilers catch some errors. No question. Further, there
is no trivial coercion of items not in the same family. That would
require either a "helper function" or an explicit coercion by the
coder.

It occurs to me that this is creating a pretty wide gap between
"typeless" and "typed" code. I can write C<sub foo> or I can write
three flavors of C<multi foo($x is ______)>

> >>On the other hand, I have *no* problem with this:
> >>
> >> sub foo(@a is Array of Int is copy) {...}
> >> ^^^^^^^
> >>doing the kind of coercive wrapping you're suggesting.
> >
> >
> > What's the difference? (Aside from the obvious, that is...)
>
> Suppose I call:
>
> foo(@x);
>
> The difference is that @a above is a lexically scoped variable into
> which the contents of the corresponding argument (@x) are copied
> (i.e. assigned). Now it's perfectly possible that the contents of
> @x can be assigned into @a, even if the argument and the parameter
> are of wildly different types.

Will you be invoking the copy constructor? If so, which one:
C<@x.COPY(@a)> or C<foreach @x { @x.push($_.COPY(})>?

Also, for C<is copy> traits, one of the early optimizations is going to
be copy-on-write, I suspect. Having the syntax for "is coerced" and "is
copy" be the same may impair some optimization (coercing when only COW
is desired for large arrays).

>
> But if I say:
>
> sub foo(@a is Array of Int) {...}
> ...
> foo(@x);
>
> then I'm saying: "within &foo, @a is just another name for @x". So
> they are (temporarily) the same thing.

> That can only (be allowed to) happen if the actual type of @x
> satisfies all the requirements of the declared type of @a.

And that's the kind of thing I want the type-checker to permit. To me,
this translates into the reverse of the "normal" strongly-typed
permissions:

"Everyone Knows" that an Int is a Scalar, and therefore a sub that has
a Scalar parameter can safely be passed an Int. This is normal.

What I want is the ability to do the opposite: Silence the warning that
occurs when I pass a Scalar to a sub expecting an Int.

=Austin


Larry Wall

unread,
Mar 13, 2003, 2:55:06 PM3/13/03
to perl6-l...@perl.org
On Thu, Mar 13, 2003 at 11:31:30AM -0800, Austin Hastings wrote:
: "Everyone Knows" that an Int is a Scalar, and therefore a sub that has

: a Scalar parameter can safely be passed an Int. This is normal.
:
: What I want is the ability to do the opposite: Silence the warning that
: occurs when I pass a Scalar to a sub expecting an Int.

I don't see a problem. Scalar == Int|Num|Str|Ref, so Scalar.isa("Int").

Larry

Paul

unread,
Mar 13, 2003, 3:00:54 PM3/13/03
to Larry Wall, perl6-l...@perl.org
> I don't see a problem. Scalar == Int|Num|Str|Ref, so
> Scalar.isa("Int").

Scalar.isa("Int") && Int.isa("Scalar") ????

__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

Damian Conway

unread,
Mar 13, 2003, 8:47:59 PM3/13/03
to Larry Wall, perl6-l...@perl.org
Larry Wall wrote:

Err, sorry, no it doesn't.

Either Scalar is the superclass of those four, in which case a Scalar isn't
sufficient to satisfy the requirements of an Int; or Scalar is a disjunction
of those four, in which case it's still not guaranteed that whatever the
Scalar is holding will satisfy the requirements of an Int.

As I said before it comes down to whether a parameter type is a specification
that:

a) The argument passed here must be something that could conceivably
not cause the program to crash and burn, and we'll verify that at
run-time if necessary.

or:

b) The argument passed here must be something that will definitely
not cause the program to crash and burn, and we'll verify that at
compile-time.

I'm arguing that the former is well-nigh useless, and that the latter is what
large systems developers and optimizer writers have been begging for. Besides
which, the latter gives you the former. If you don't care about compile-time
type checking, declare the *parameter* as Scalar, and let it accept anything.

But if type-specialized parameters are allowed to take *anything*, then
they're nigh on worthless, except as a (third) coercion mechanism (in addition
to explicit int($x), +$x, ~$x; and implicit contextual coercions).

Damian

Brent Dax

unread,
Mar 13, 2003, 10:36:00 PM3/13/03
to Damian Conway, Larry Wall, perl6-l...@perl.org
Damian Conway:
# a) The argument passed here must be something that
# could conceivably
# not cause the program to crash and burn, and
# we'll verify that at
# run-time if necessary.
#
# or:
#
# b) The argument passed here must be something that will
# definitely
# not cause the program to crash and burn, and
# we'll verify that at
# compile-time.
#
# I'm arguing that the former is well-nigh useless, and that
# the latter is what
# large systems developers and optimizer writers have been
# begging for. Besides
# which, the latter gives you the former. If you don't care
# about compile-time
# type checking, declare the *parameter* as Scalar, and let it
# accept anything.

IMO, the semantics should be something like this:

Assume F is the type of the formal parameter and A is the type
of the actual parameter.
If A isa F, we're fine.
If F isa A, defer type checking to runtime.
Else, pitch a fit.

Since the default type is Object and any type isa Object, untyped formal
parameters are "checked" at compile time and untyped actual parameters
are checked at runtime.

That's the semantics for strict typing, anyway...

# But if type-specialized parameters are allowed to take
# *anything*, then
# they're nigh on worthless, except as a (third) coercion
# mechanism (in addition
# to explicit int($x), +$x, ~$x; and implicit contextual coercions).

What's wrong with that? It gives us a way to ensure that we have a
suitable object without having to remember to check ourselves.

I think that there should be two types of arg typing[1]: 'strict' and
'loose'. Strict arg typing doesn't coerce, except to turn subclasses
into superclasses; loose arg typing, on the other hand, coerces whenever
possible. The mechanism for choosing between strict and loose arg
typing should be under the caller's control, not the callee's. (The
callee decides what types they want, and the caller decides how to
create those types. This seems consistent with Perl's philosophy of
being flexible and making B&D optional.)

Strict arg typing should be under the control of the 'strict' pragma,
and should be in strict's default export list. This would mean that the
B&D would be off by default but easy to turn on.

[1] Perhaps this should be generalized to strict and loose coercion; I'm
not really sure yet. (Under strict coercion, adding an Int and a Str
would be an error, instead of coercing the Str into an Int.)

--Brent Dax <bren...@cpan.org>
@roles=map {"Parrot $_"} qw(embedding regexen Configure)

>How do you "test" this 'God' to "prove" it is who it says it is?
"If you're God, you know exactly what it would take to convince me. Do
that."
--Marc Fleury on alt.atheism

Dave Whipp

unread,
Mar 13, 2003, 10:36:43 PM3/13/03
to perl6-l...@perl.org
Damian Conway wrote:
> b) The argument passed here must be something that will definitely
> not cause the program to crash and burn, and we'll verify
> that at
> compile-time.
>
> I'm arguing that the former is well-nigh useless, and that the latter is
> what large systems developers and optimizer writers have been begging
> for. Besides which, the latter gives you the former. If you don't care
> about compile-time type checking, declare the *parameter* as Scalar, and
> let it accept anything.

Module writers should always write their code to high standards of
safety. Client code, OTOH, should be able to be more sloppy --
especially for one-liners. But I agree that (b) should be the goal: I
agree run-time type checking is "well-nigh useless".

But, the definition of compile-time checking can be broad. Firstly, we
should distinguish between things that are explicitly typed, and things
that are not. If a user does not specify the type of a value, then the
compiler should make a best-effort attempt to infer the type from what
it does know. e.g.

sub foo () returns Int { 6 }
sub bar (Int $a) { print $a+1 }
...
my $a = foo;
bar($a);

In this case, the user has chosen not to give an explicit type to $a,
but a trivial dataflow analysis will determine that type. The default
mode (in non-strict contexts) should be to infer the type.

I just re-read A1, the RFC:16 discussion. Larry seems to say that,
outside of class definitions, strictness will not be the default. Such
code is never very large (well, OK, I've seen some bad scripts: but hard
cases make bad law), so we can afford to let the compiler do some work
to do all the necessary inference ... and even insert coercion code if
necessary. If performance becomes an issue, then the script writer will
clean up the code.

A quick list of the possible cases -- the compiler might determine (via
type inference) that a binding is:

definitely not safe: can't even be coerced -- always an error
definitely safe, no corercion needed -- always OK
definitely safe if we corerce -- OK in non-strict contexts
undecidable -- error in strict contexts, else compile-time warning

Which brings us back to the question of what is coercable. I'd say that
we should only permit implicit coercion where a user has not used
explicit typing; but there should be a simple prefix operator to enable
coercion of typed things. Perhaps the "splat" operator could serve here:

my Str $a = <>;
my Int $b = $a; # Error -- $a is explicitly typed as Str
my Int $c = $a.num # OK -- explicit corcion
my Int $d = *$a; # OK -- splat permits implicit coercion

Dave.
--
http://dave.whipp.name

Anton Berezin

unread,
Mar 13, 2003, 4:52:04 PM3/13/03
to Damian Conway, perl6-l...@perl.org
On Thu, Mar 13, 2003 at 10:21:25PM +1100, Damian Conway wrote:

> By distinguishing a parameter that *requires* a particular type, from
> a parameter that *ensures* a particular type (by coercion if
> necessary). I've suggested that using C<is copy> semantics should
> indicate "make whatever I'm actually given into one of these, if you
> can", whereas reference semantics say "this must already be one of
> these" (by their very nature, since an aliased parameter *is* the
> argument).

It looks like a lot of people is going to use "is copy" a lot. Correct
me if I am wrong, but I see the default "is constant" trait largely as a
speed optimization. Would not it be more correct from the point of view
of good Huffman coding, that "is copy" is made the default, and let the
module authors and large project implementors to bother with "is
constant" when they deem it necessary? After all, this will be a small
added burden for folks who go the "type and trait everything" route. An
extra bonus of making "is copy" the default would be the ability to
modify a parameter locally (Huffman coding again here for short program
writers).

=Anton.

Larry Wall

unread,
Mar 14, 2003, 12:43:46 PM3/14/03
to perl6-l...@perl.org
On Thu, Mar 13, 2003 at 10:52:04PM +0100, Anton Berezin wrote:

Well, at minimum there could certainly be a "use sig copy" pragma or
some such. But I don't think "is copy" will be used all that much if
we set up "is constant" with the right semantics in the first place,
which is that it can do a copy of basic types if it feels like it.
It's only "is rw" or "is ref" that really require reference semantics,
and then we're talking proxies if we want any coercion.

Larry

Larry Wall

unread,
Mar 14, 2003, 1:08:15 PM3/14/03
to perl6-l...@perl.org
On Thu, Mar 13, 2003 at 07:36:00PM -0800, Brent Dax wrote:
: I think that there should be two types of arg typing[1]: 'strict' and

: 'loose'. Strict arg typing doesn't coerce, except to turn subclasses
: into superclasses; loose arg typing, on the other hand, coerces whenever
: possible. The mechanism for choosing between strict and loose arg
: typing should be under the caller's control, not the callee's. (The
: callee decides what types they want, and the caller decides how to
: create those types. This seems consistent with Perl's philosophy of
: being flexible and making B&D optional.)

Precisely. The parameter types are completely invariant for the
callee. They are optionally invariant for the caller depending on some
kind of stricture. But I darn well want the naive user to be able to
pass a Scalar to an Int parameter and have it DWTM without them knowing
a blessed thing about these mysterious entities called "classes".

We've got to keep the entry ramp low, or Perl is no longer Perl.

The real question is whether this particular stricture is part of the
default "use strict" that classes and modules assume. There are
decent arguments on both sides of that one, but just to mollify Damian
I'm inclined to come down on the strict side for that.

This week. :-)

Larry

Michael Lazzaro

unread,
Mar 14, 2003, 2:06:43 PM3/14/03
to perl6-l...@perl.org
OK, divide & conquer. We seem to be spasming about this and trying to
talk about N things at once, so here's an issue summary. We're talking
about at least two separate cases, (1) "inferring" type where none has
been specified, and (2) "coercing" a typed value into another type.
Let's take these separately, first one first:

[ISSUE: Type Inference]

Consider the following example:

class MyClass {...}
sub foo(MyClass $c) {...}

my MyClass $a = MyClass.new;
my $b = $a;

foo($a); # OK
foo($b); # ERROR!

The issue here is that the variable C<$b> has not been typed -- or more
accurately, it has been given a default type of C<Scalar>. So $b can
store a MyClass, but it has not been _guaranteed_ to contain a MyClass,
violating the typed signature of C<foo>.

This is potentially significant because for quick-and-dirty scripts,
some programmers would prefer to not explicitly type every single
variable they declare, but they would still like to be able to use
modules that have been written to be type-aware.

NOTE that this issue only comes up when passing "untyped" vars like
C<my $b>, C<my @b>, or C<my %b>. If a type _has_ been explicitly
given, it's not type "inference", it's type "coercion", which should be
considered separately.


[POSSIBLE APPROACHES]

1) If an "untyped" var is used for a typed parameter, it's a
compile-time error. Broadly speaking, if you use type-aware subs, you
must use type awareness everywhere.

The advantage of this approach is that in assures there will be no
hidden runtime costs; if you want to take advantage of the *huge* speed
increases of using strictly typed vars, you can just do it. The
disadvantage is that you'd pretty much have to use types *everywhere*
in your program, or *nowhere*, because the edge between them always
introduces compile-time errors. This is especially troublesome when
using library modules, for example.


2) If an "untyped" var is used for a typed parameter, invoke runtime
type checking, either with or without a warning (according to a pragma?)

The advantage of this approach is that it will silently work; the
disadvantage is that it could introduce *very* expensive runtime
penalties if "accidentally" invoked, essentially nullifying the speed
gains of typing.


3) If an "untyped" var is used for a typed parameter, a simple dataflow
analysis is used to determine whether the compiler can guarantee that,
at that point, an "untyped" var will _always_ contain values of a
known, specific type. If so, the type is inferred (silently or with a
warning, according to pragma?) Otherwise, it is a compile-time error.

The advantage is that it will silently work, and will present no
runtime speed penalties. The disadvantage is that different
implementations of Perl might have different levels of dataflow
analysis, causing one-off code that was acceptable under a "smarter"
analysis to be invalid if moved to a "dumber" analysis -- meaning
either this has to be acceptable behavior of an implementation, or that
we need to specify, as part of the language spec, the precise smartness
of the analysis.

---

AFAICT, these are the *only* possible solutions to the problem. At
last count, Larry was leaning towards #2. Damian was countering with
#1. Some Lowly Grubs were suggesting #3. Am I missing anything?

I think we can decide _this_ issue independently of coercion issues,
yes?

MikeL

Angel Faus

unread,
Mar 14, 2003, 2:27:51 PM3/14/03
to Michael Lazzaro, perl6-l...@perl.org
Friday 14 March 2003 20:06, Michael Lazzaro wrote:
> 3) If an "untyped" var is used for a typed parameter, a simple
> dataflow analysis is used to determine whether the compiler can
> guarantee that, at that point, an "untyped" var will _always_
> contain values of a known, specific type. If so, the type is
> inferred (silently or with a warning, according to pragma?)
> Otherwise, it is a compile-time error.

Uhh.. I am emphatically against this. We cannot depend on the compiler
data-flow analysis because:

- It won't be very good (because of perl's ultra-dynamism)

- It may improve as we put more effort on the compiler, so some
program that works in perl6.4 may fail on perl6.2

- It may degrade as we add more (evil) features on the language. (Or
otherwise it may prevent us from adding more (evil) features in the
language in some future without breaking BC)

- It's hard to predict.

I would rather have an allways-complain or an allways-forgive policy,
than to have a complain-or-forgive-depending-on-the-weather-
-in-Canary-Islands policy.

-angel

Paul

unread,
Mar 14, 2003, 3:00:20 PM3/14/03
to perl6-l...@perl.org

I apologize for not including a previous message thread -- I
fumble-fingered myself out of all the relevant ones....

Still, I'd just like to cast my tiny vote regarding inferences.

I'd like to be able to write classes that can take advantage of
screaming speed, and types contribute.

I'd like to be able to tell my main script that it is required to pass
exact types, so that it gets those advantages. (I'd like something like
"use strict;" without specifying "strict what" to default to "strict
everything".)

I'd still like to be able to use my module in quick one-off's without
having it break and die if I just pass it whatever default variable I
can manage not to declare.

Wouldn't I be able to do that by writing my module with something like

multi foo (Int $i) {...}
multi foo (Scalar $s) {...}

???

Yes, I realize most people don't want to write seven versions of every
sub; but most of the time, my module is either going to be generic
enough that I'm not really as interested in the screaming end of speed,
or specific enough that I don't expect to use it for many one-off's, or
worth a little extra code.

Still, as a best-case, I'd like for the strict()-ness of my main
program to drive the determinance. If it's a slopjob quickie, I don't
*CARE* about greatspeed. Just DWIM and gimme what I want. If I want
fast code it's worth being careful, so I'll use strict and expect it to
gripe and expire if I didn't do something quite right.

So, my vote is, do the run-time checking if not strict; restrict me to
compile-time compliance under the strict pragma.

To be honest, I'd also kinda like to see a coerce-it-if-you-can
behavior, but I'd rather have that as a pragma as well. If it's worth
all the extra work to find an appropriate coersion, it's worth a few
extra keystrokes..... but I could live without all that quite happily.

Am I off base?

Michael Lazzaro

unread,
Mar 14, 2003, 3:14:41 PM3/14/03
to Michael Lazzaro, perl6-l...@perl.org

On Friday, March 14, 2003, at 11:06 AM, Michael Lazzaro wrote:
> AFAICT, these are the *only* possible solutions to the problem. At
> last count, Larry was leaning towards #2. Damian was countering with
> #1. Some Lowly Grubs were suggesting #3. Am I missing anything?

Whoops! That needs correcting. Larry sayeth:


> The real question is whether this particular stricture is part of the
> default "use strict" that classes and modules assume. There are
> decent arguments on both sides of that one, but just to mollify Damian
> I'm inclined to come down on the strict side for that.

E.G. "use strict" invokes #1, otherwise it's #2. Sorry for the
repost, but if something says "SUMMARY" it'd be best for it not to be
obsolete after the first 5 min...

MikeL

Dave Whipp

unread,
Mar 14, 2003, 3:21:50 PM3/14/03
to perl6-l...@perl.org
Michael Lazzaro wrote:
> 3) If an "untyped" var is used for a typed parameter, a simple dataflow
> analysis is used to determine whether the compiler can guarantee that,
> at that point, an "untyped" var will _always_ contain values of a known,
> specific type. If so, the type is inferred (silently or with a warning,
> according to pragma?) Otherwise, it is a compile-time error.

I was suggesting something slightly more subtle:

* If the dataflow says its definitely wrong, then its an error

* If the dataflow says its definitely OK, it is OK -- no warning,
nor error, nor run-time check

* Otherwise (silently or with warning), defer to run-time check

The coercsion aspect makes it slightly more complex, but not
significantly so.

Michael Lazzaro

unread,
Mar 14, 2003, 4:19:41 PM3/14/03
to Dave Whipp, perl6-l...@perl.org

Sorry -- I agree 100% with that. As a language feature, it has
problems, as Luke & Angel both pointed out. As a mere optimization of
case #2, it's fine, and there's not really any language implications.
In tight loops, etc., it would probably speed up the runtime quite a
bit, in fact.

The optional warning if it falls through to the runtime check is
important, IMO, because even lazy one-off scripts sometimes need to be
fast. :-/

It would still mean that, in 'non-strict' mode, you might get different
"deferring to runtime typechecking" warnings on different versions of
P6, but I don't see that as a big issue, if it's just an optional
warning.

MikeL

Erik Steven Harrison

unread,
Mar 14, 2003, 9:38:43 PM3/14/03
to perl6-l...@perl.org, Larry Wall

--

On Fri, 14 Mar 2003 10:08:15
Larry Wall wrote:
>On Thu, Mar 13, 2003 at 07:36:00PM -0800, Brent Dax wrote:
>: I think that there should be two types of arg typing[1]: 'strict' and
>: 'loose'. Strict arg typing doesn't coerce, except to turn subclasses
>: into superclasses; loose arg typing, on the other hand, coerces whenever
>: possible. The mechanism for choosing between strict and loose arg
>: typing should be under the caller's control, not the callee's. (The
>: callee decides what types they want, and the caller decides how to
>: create those types. This seems consistent with Perl's philosophy of
>: being flexible and making B&D optional.)
>
>Precisely. The parameter types are completely invariant for the
>callee. They are optionally invariant for the caller depending on some
>kind of stricture. But I darn well want the naive user to be able to
>pass a Scalar to an Int parameter and have it DWTM without them knowing
>a blessed thing about these mysterious entities called "classes".
>
>We've got to keep the entry ramp low, or Perl is no longer Perl.
>

>The real question is whether this particular stricture is part of the
>default "use strict" that classes and modules assume. There are
>decent arguments on both sides of that one, but just to mollify Damian
>I'm inclined to come down on the strict side for that.
>

I'll put my vote down on strictness (as in "complain about mismatch
as soon as possible") by default.


But just as prototypes are ignored when we prepend & in Perl 5 can't
some similar frobobnitz say "But on this here call, wait till runtime
and coerce if needed"? After all, it's not the kind of thing a callee
should dictate, but the caller.


>This week. :-)

I'm easy.

-Erik
>
>Larry
>


____________________________________________________________
Get 25MB of email storage with Lycos Mail Plus!
Sign up today -- http://www.mail.lycos.com/brandPage.shtml?pageId=plus

Nicholas Clark

unread,
Mar 15, 2003, 6:48:12 PM3/15/03
to perl6-l...@perl.org
There seems to be some confusion about which way up the world is.

On Tue, Mar 11, 2003 at 01:25:41PM +1100, Damian Conway wrote:
> Which in turn is because:
>
> not Scalar.isa(int)

On Thu, Mar 13, 2003 at 11:55:06AM -0800, Larry Wall wrote:
> On Thu, Mar 13, 2003 at 11:31:30AM -0800, Austin Hastings wrote:
> : "Everyone Knows" that an Int is a Scalar, and therefore a sub that has
> : a Scalar parameter can safely be passed an Int. This is normal.

> I don't see a problem. Scalar == Int|Num|Str|Ref, so Scalar.isa("Int").
>
> Larry

On Thu, Mar 13, 2003 at 12:00:54PM -0800, Paul wrote:
> > I don't see a problem. Scalar == Int|Num|Str|Ref, so
> > Scalar.isa("Int").
>

> Scalar.isa("Int") && Int.isa("Scalar") ????

This term "subtyping" I didn't know:

On Fri, Mar 14, 2003 at 10:46:31AM -0800, Larry Wall wrote:

> Well, I'm using the terms type and class pretty much interchangeably
> there, so don't put too much weight on that. But Perl 6 may well
> distinguish classes from types. Classical classes can only add
> capabilities when you derive (yes, you can redefine methods to do less,
> but the interface is still there). When I think of types, I'm thinking
> of parameterizing a class with some constraints. In the Ada world,
> that's called "subtyping". Whether these contraints are mathematical
> ("must be an even number") or structural ("must have dimensions
> (3,3,3)"), they still cut down on the allowed set of values for the
> base type.

I'm not sure if it helps people understand why I'm confused by explaining
my background, but I've done exactly zero computer science, and have come
to whatever (mis)understanding of OO I have by using C++ (and then perl).
I've never used Java, but I'm aware that it has a concept of "interfaces"
that can cleanly fulfil most of what C++ programmers end up using multiple
inheritance for.

Back to perl:

I admit that I think of Scalar as being something that conceptually
multiply inherits from String, Num, Int and "other" (ref), because a scalar
can be any of these things as it feels necessary. Being from somewhat a C++
background (oh, and knowing how SVs work in perl5's source), I don't really
feel that the tree goes the other way, with Scalar at the top. I don't
expect Int to have a (in perl5-speak) .= method, but if Int inherits from
Scalar, it's going to have to, isn't it?

This reminds me of a conundrum that was explained to me - sub-classing
geometry:

Presumably a Circle class has an attribute radius, which can be set and
retrieved. And Ellipse has two, semi-major axis and semi-minor axis.
A circle is an ellipse where the semi-major and semi-minor axes are equal.

So, if you implement Circle as a subclass of Ellipse this is fine - Circle
provides the radius methods, so there's no way an Ellipse can have one called
on it. Good. And if you call Ellipse's semi-major axis method on a Circle
you get back the same value as radius (or semi-minor axis)

But what happens if take a Circle (isa Ellipse) and try to set the semi-major
axis to a value that differs from the semi-minor axis? (I don't know. It
violates something as I understand it)


Conversely, if you implement Ellipse as a subclass of Circle, this problem is
solved. There are no semi-major or semi-minor axes methods, so no chance of
going wrong there. But as Ellipse isa Circle, it will inherit Circle's radius
method. What should it return the radius of an Ellipse? (I don't know)

I don't know the answer to which way up they go. As I understand it, there
is no right answer.

Likewise I'm not convinced about which way round the scalar types heirachy
goes in perl6. I like it to be both ways up at the same time.
(in the same universe please)
Then again, this week is the subroutines apocalypse, not the objects
apocalypse, so hopefully all will become clear in a subsequent this week.

Nicholas Clark

Luke Palmer

unread,
Mar 15, 2003, 7:46:51 PM3/15/03
to ni...@unfortu.net, perl6-l...@perl.org
> I'm not sure if it helps people understand why I'm confused by explaining
> my background, but I've done exactly zero computer science, and have come
> to whatever (mis)understanding of OO I have by using C++ (and then perl).
> I've never used Java, but I'm aware that it has a concept of "interfaces"
> that can cleanly fulfil most of what C++ programmers end up using multiple
> inheritance for.

(But not all of it; eg. design-by-policy)

> Back to perl:
>
> I admit that I think of Scalar as being something that conceptually
> multiply inherits from String, Num, Int and "other" (ref), because a scalar
> can be any of these things as it feels necessary. Being from somewhat a C++
> background (oh, and knowing how SVs work in perl5's source), I don't really
> feel that the tree goes the other way, with Scalar at the top. I don't
> expect Int to have a (in perl5-speak) .= method, but if Int inherits from
> Scalar, it's going to have to, isn't it?

Good observation. I agree. The problem is that there's a Liskov
substitution that's tripping people up. An Int can be given to a
Scalar argument, so it must be a subclass.

It may be that Scalar is unrelated, inheritance-wise, to the four
basic types. Rather, it aggregates and delegates, and provides
conversions to and from them.

> Presumably a Circle class has an attribute radius, which can be set and
> retrieved. And Ellipse has two, semi-major axis and semi-minor axis.
> A circle is an ellipse where the semi-major and semi-minor axes are equal.
>
> So, if you implement Circle as a subclass of Ellipse this is fine - Circle
> provides the radius methods, so there's no way an Ellipse can have one called
> on it. Good. And if you call Ellipse's semi-major axis method on a Circle
> you get back the same value as radius (or semi-minor axis)
>
> But what happens if take a Circle (isa Ellipse) and try to set the semi-major
> axis to a value that differs from the semi-minor axis? (I don't know. It
> violates something as I understand it)
>
>
> Conversely, if you implement Ellipse as a subclass of Circle, this problem is
> solved. There are no semi-major or semi-minor axes methods, so no chance of
> going wrong there. But as Ellipse isa Circle, it will inherit Circle's radius
> method. What should it return the radius of an Ellipse? (I don't know)
>
> I don't know the answer to which way up they go. As I understand it, there
> is no right answer.

Well, I think this is one of those subtyping things Larry is talking
about (Circle doesn't I<extend> Ellipse, it puts a constraint on
it). But in the most generic world, I would not subclass either. I
might implement Circle with an Ellipse (spelled C<private> in C++),
but their interfaces are incompatible as you've described. You can't
use an Ellipse as a Circle, because it has no radius, and you can't go
the other way around, because Circle's can't change their semi-major
axis.

I know this is just a silly example, but in my experience, a common
design mistake is to use inheritance too liberally. This is a good
demonstration of that. Circle and Ellipse should be siblings in this
family (perhaps derived from Shape).

> Likewise I'm not convinced about which way round the scalar types heirachy
> goes in perl6. I like it to be both ways up at the same time.
> (in the same universe please)
> Then again, this week is the subroutines apocalypse, not the objects
> apocalypse, so hopefully all will become clear in a subsequent this week.

The more I think about this design, the more I like the solution of no
inheritance relation between them. I would implement Scalar as a
proxy to whatever lies underneath, stored as references (perhaps) so
changes would be reflected back.

class Scalar
{
submethod BUILD($.num is rw) { }
submethod BUILD($.str is rw) { }
submethod BUILD($.ref is rw) { }

method infix:+= ($self: $rhs) {
$.num += $rhs.num;
$.str = $.ref = Undef;
$self;
}

multi infix:as(Scalar $self, Class(Num) $class)
returns(Num) is rw {
return my $x is Proxy(
for => $.num,
STORE => { $.str = $.ref = Undef; $num = $_ })
} # Using the multimethod, um, method for auto conversion

# ...

has Num|Undef $.num;
has Str|Undef $.str;
has Ref|Undef $.ref;
};

Or something like that, maybe.

Luke

gre...@focusresearch.com

unread,
Mar 16, 2003, 9:20:48 AM3/16/03
to Nicholas Clark, perl6-l...@perl.org
Nick --

I've been thinking of it like this:

class int isa intlike; # and isa value or whatever
class Int isa intlike; # and isa Object or whatever

class num isa numlike; # and isa value or whatever
class Num isa numlike; # and isa Object or whatever

...

class Scalar isa intlike, numlike, ...; # and isa Object or whatever

The *like classes are placeholder (interface) classes that capture
the ability to *treat as* (as opposed to *really is*). And (importantly
IMO) the *like classes capture the aspects of foo and Foo that
are the same, leaving the foo and Foo classes to capture the
differences.

- - - - - - - - - - - - - - - - - - - - - -

A different way to handle Scalar would be to have Perl 6 allow
mutating objects (type changes). Scalar could be a superclass
of Int and Num, but it could be abstract (no instances allowed).
Values would be only of concrete types (e.g. Int, Num, Str). That
isn't very tricky, until you try to do

my $x = "5";
$x += 37;

Is $x the same object as it was before, but with a different internal
state, or is it now a completely different object (since its type
presumably changed to Int from Str)?

I'm not as comfortable with this, I have to admit. I like a single Scalar
class that has magical internal state and presents various "facets"
to the outside world (Int, Num, Str, ...). The magical internal state
can do things like maintain Int and Str values in parallel if it wishes
(which would be a strange thing for Int to do by itself, IMO).

- - - - - - - - - - - - - - - - - - - - - - - -

FWIW, I think Circle is a predicate on the set of Ellipses, so I'd say
Circle isa Ellipse. If I were using Eiffel, I'd use renaming and
overriding so that you could query the semimajor and semiminor
axes and radius, and get the same number in all three cases.
And, if you are working with a value that isa Circle, you may not
*set* the semimajor and semiminor axes (you have to set the
radius). Of course, this makes things ugly when you have a Circle
in an Ellipse variable. You'd like to take the fact that something
is an Ellipse to mean that you *can* set the axes separately,
since that is an Ellipse kind of thing to do. This smacks of an
argument for Ellipse and Circle being peers -- subclasses of
some other abstract helper class.

Or, to truly simplify, don't have a Circle class at all. Have an isCircle
predicate on Ellipse, which matches the mathematical case nicely.
Being able to do C<my $foo = new Circle 137> and get back an
appropriate ellipse would still be nice, though.

I think the trouble comes from the fact that there are two kinds of
Circle that come up: Ellipse-in-general-but-Circle-right-now (loose,
or behavioral) and Circle-we-want-to-treat-as-Ellipse-sometimes
(strict, classical). You could definitely make an Ellipse class that
had an isCircle method, a radius-based constructor, and
getRadius and setRadius methods, and the only time it would let
you down is if you didn't check isCircle before calling getRadius,
because it would throw an exception (since there is no single
radius for a not isCircle Ellipse). It means your class is really
EllipseOrCircle, I guess.

For the strict case above, you would have to have though
ahead enough to have the Ellipse-like 'get' methods in one
place and teh Ellipse-like 'set' methods elsewhere. Circle
could inherit from the EllipseGettable but not EllipseSettable
(since you can ask for, but not set the semi axes).


Regards,

-- Gregor Purdy


Nicholas Clark <ni...@unfortu.net>
03/15/2003 06:48 PM


To: "perl6-l...@perl.org" <perl6-l...@perl.org>
cc:
Subject: Re: A6: Complex Parameter Types

I'm not sure if it helps people understand why I'm confused by explaining


my background, but I've done exactly zero computer science, and have come
to whatever (mis)understanding of OO I have by using C++ (and then perl).
I've never used Java, but I'm aware that it has a concept of "interfaces"
that can cleanly fulfil most of what C++ programmers end up using multiple
inheritance for.

Back to perl:

I admit that I think of Scalar as being something that conceptually
multiply inherits from String, Num, Int and "other" (ref), because a
scalar
can be any of these things as it feels necessary. Being from somewhat a
C++
background (oh, and knowing how SVs work in perl5's source), I don't
really
feel that the tree goes the other way, with Scalar at the top. I don't
expect Int to have a (in perl5-speak) .= method, but if Int inherits from
Scalar, it's going to have to, isn't it?

This reminds me of a conundrum that was explained to me - sub-classing
geometry:

Presumably a Circle class has an attribute radius, which can be set and


retrieved. And Ellipse has two, semi-major axis and semi-minor axis.
A circle is an ellipse where the semi-major and semi-minor axes are equal.

So, if you implement Circle as a subclass of Ellipse this is fine - Circle
provides the radius methods, so there's no way an Ellipse can have one
called
on it. Good. And if you call Ellipse's semi-major axis method on a Circle
you get back the same value as radius (or semi-minor axis)

But what happens if take a Circle (isa Ellipse) and try to set the
semi-major
axis to a value that differs from the semi-minor axis? (I don't know. It
violates something as I understand it)


Conversely, if you implement Ellipse as a subclass of Circle, this problem
is
solved. There are no semi-major or semi-minor axes methods, so no chance
of
going wrong there. But as Ellipse isa Circle, it will inherit Circle's
radius
method. What should it return the radius of an Ellipse? (I don't know)

I don't know the answer to which way up they go. As I understand it, there
is no right answer.

Likewise I'm not convinced about which way round the scalar types heirachy


goes in perl6. I like it to be both ways up at the same time.
(in the same universe please)
Then again, this week is the subroutines apocalypse, not the objects
apocalypse, so hopefully all will become clear in a subsequent this week.

Nicholas Clark

Luke Palmer

unread,
Mar 17, 2003, 1:35:43 PM3/17/03
to gre...@focusresearch.com, ni...@unfortu.net, perl6-l...@perl.org
> Nick --
>
> I've been thinking of it like this:
>
> class int isa intlike; # and isa value or whatever
> class Int isa intlike; # and isa Object or whatever
>
> class num isa numlike; # and isa value or whatever
> class Num isa numlike; # and isa Object or whatever
>
> ...
>
> class Scalar isa intlike, numlike, ...; # and isa Object or whatever
>
> The *like classes are placeholder (interface) classes that capture
> the ability to *treat as* (as opposed to *really is*). And (importantly
> IMO) the *like classes capture the aspects of foo and Foo that
> are the same, leaving the foo and Foo classes to capture the
> differences.

This is an interesting concept. We can have Intlike and Numlike
abstract class that Int and Num concretify :). Then anything can
derive from Intlike and Numlike and be substituted where they are
expected everywhere.

But this will be a common enough operation (behaving like a superclass
but not actually being one) that there might as well be more intrinsic
support for it:

class myInt is interfaced(Int) {...}

myInt must override all of Int's methods, and it inherits none of its
data. It might even register in the inheritance heirarchy. I don't
have enough experience with this technique to know whether or not it
should.

That would be a neat way to do interfaces: allow a concrete type to
act as an interface. It would also be a neat way to do Scalar.

Hmmm....

Luke

Michael Lazzaro

unread,
Mar 17, 2003, 1:58:35 PM3/17/03
to Luke Palmer, perl6-l...@perl.org

On Monday, March 17, 2003, at 10:35 AM, Luke Palmer wrote:
>> I've been thinking of it like this:
>>
>> class int isa intlike; # and isa value or whatever
>> class Int isa intlike; # and isa Object or whatever
>>
>> class num isa numlike; # and isa value or whatever
>> class Num isa numlike; # and isa Object or whatever
>>
>> ...
>>
>> class Scalar isa intlike, numlike, ...; # and isa Object or whatever
>>
>> The *like classes are placeholder (interface) classes that capture
>> the ability to *treat as* (as opposed to *really is*). And
>> (importantly
>> IMO) the *like classes capture the aspects of foo and Foo that
>> are the same, leaving the foo and Foo classes to capture the
>> differences.
>
> This is an interesting concept. We can have Intlike and Numlike
> abstract class that Int and Num concretify :). Then anything can
> derive from Intlike and Numlike and be substituted where they are
> expected everywhere.

Not quite sure I understand -- how does this interact with the goal of
flexibility between things which take an C<Int> vs. an C<int>, for
example? Ideally...

sub foo(int $n) {...} # accepts int only (?)
sub foo(Int $n) {...} # accepts int, Int (?)
sub foo(num $n) {...} # accepts int, num (?)
sub foo(Num $n) {...} # accepts int, Int, num, Num (?)

(Assuming a significant difference between int/Int and num/Num is that
the latters are allowed to be undef, thus making the above distinctions
more relevant)

You certainly don't want to have to type things as

sub foo(intlike $n) {...}

just to get the above flexibility, for example.

?

MikeL

Dave Whipp

unread,
Mar 17, 2003, 2:11:52 PM3/17/03
to perl6-l...@perl.org
> class Scalar isa intlike, numlike, ...; # and isa Object or whatever

Qoting A6: "Perl makes a distinction between the type of the variable,
and the type of the value"

If we view Scalar as the type of a variable, not value, then we could
cease to need all this cleverness with inheritance. People are assuming that

my $a;

is equivalent to

my Scalar $a is Scalar;

I would propose that it, instead, means

my __implicitly_typed__ $a is Scalar;

Furthermore, if you explicitly define a value to be of Scalar type, then
this means that it is a container that holds exactly one value -- there
should be no expectation that it should behave like an integer, etc. So

my Scalar $a = 1;

should be an error -- you'd have to call the STORE method explicitly to
store the value into the scalar value.


Dave.
--
http://dave.whipp.name

0 new messages