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

clarifying the spec for 'ref'

4 views
Skip to first unread message

Mark Stosberg

unread,
Aug 23, 2006, 6:20:55 PM8/23/06
to perl6-l...@perl.org
I noticed in pugs, 'ref' does not return 'HASH' and 'ARRAY' as Perl5
does, but returns values including 'Hash', 'Array' and 'Array::Const'.

I don't find meaningful mentions of 'HASH' and 'ARRAY' by grep'ing
docs/Perl6 (or even "ref"!), so I wanted to check in here about the
meaningfulness of this change.

Personally, I dislike the change from HASH to 'Hash' because it seems to
be change without a significant benefit. It's annoyingly different.

The ARRAY case is worse, because it seems I now need to write this:

if ref $a eq any('Array','Array::Const') {

If you are interested, here's code which illustrates cases when
'Array' is returned, versus 'Array::Const'

my $a = [<a>];
my $b = \@('b');

say ref $a;
say ref $b;

I'd like for 'HASH' and 'ARRAY' to keep working, or for the Perl6 docs
to justify the change.

Mark

Larry Wall

unread,
Aug 23, 2006, 7:06:01 PM8/23/06
to perl6-l...@perl.org
On Wed, Aug 23, 2006 at 06:20:55PM -0400, Mark Stosberg wrote:
: I noticed in pugs, 'ref' does not return 'HASH' and 'ARRAY' as Perl5

: does, but returns values including 'Hash', 'Array' and 'Array::Const'.

Well, first of all, ref is going away entirely, since there's no such
thing as a reference in Perl 6 (or everything is a reference, looking
at it the other way), so in a boolean context it would be trivially
true (or false) for everything. Its use to return the type of the
referent is then completely misnamed.

And even for that use, returning a string is wrong. It should return
the type itself, and that should stringify to the type name if you use
it in string context. Which mostly you don't want to.

Anyway, .ref is likely to end up with a name like .what or .WHAT
instead. (And .SKID is probably changing to .WHO or .WHICH at the
same time. And maybe .META gets renamed .HOW while we're at it.
Maybe .WHERE gives you the memory address, or the url, or something.
As for .WHY, well, .WHYNOT? :-)

: I don't find meaningful mentions of 'HASH' and 'ARRAY' by grep'ing


: docs/Perl6 (or even "ref"!), so I wanted to check in here about the
: meaningfulness of this change.

It's meaningful. There is no longer a distinction between fake types
naming internal forms and real types. So we renamed the formerly fake
types to look more like real types, because they are.

: Personally, I dislike the change from HASH to 'Hash' because it seems to


: be change without a significant benefit. It's annoyingly different.

Oh, if that's your smallest annoyance, please count yourself lucky. :)

: The ARRAY case is worse, because it seems I now need to write this:


:
: if ref $a eq any('Array','Array::Const') {

You're in P5-think here in assuming that type names are merely strings.
In P6-think you just want to treat the type as a first class object. In
particular, smart match already does what you want and handles inheritance
for you. Instead of saying

if ref $a eq 'ARRAY' {

you really want:

if $a ~~ Array {

and that also matches Array::Const, assuming it's derived from Array.

: If you are interested, here's code which illustrates cases when


: 'Array' is returned, versus 'Array::Const'
:
: my $a = [<a>];
: my $b = \@('b');
:
: say ref $a;
: say ref $b;
:
: I'd like for 'HASH' and 'ARRAY' to keep working, or for the Perl6 docs
: to justify the change.

The justifications are mostly off in the Apocalypses. Use of
types in smartmatching is mostly discussed in A04, for instance.
But the synopses are primarily intended contain just the changes,
not the rationale for the changes.

Larry

Luke Palmer

unread,
Aug 24, 2006, 1:41:05 AM8/24/06
to perl6-l...@perl.org
On 8/23/06, Larry Wall <la...@wall.org> wrote:
> you really want:
>
> if $a ~~ Array {
>
> and that also matches Array::Const, assuming it's derived from Array.

Well, actually Array would be a subtype of Array::Const, not t'other
way round. That is a little bit disconcerting, because when you say
$a ~~ Array, you probably want the const ones too. I'm not in a state
of mind to resolve the issue at the moment, but I'll happily point it
out. :-)

Luke

Mark J. Reed

unread,
Aug 24, 2006, 6:15:19 PM8/24/06
to Luke Palmer, perl6-l...@perl.org
On 8/24/06, Luke Palmer <lrpa...@gmail.com> wrote:
> Well, actually Array would be a subtype of Array::Const, not t'other
> way round.

Why? That makes no sense to me. An Array isn't a variety of
constant Array; a constant Array is a variety of Array...

Anyway, while smart matching with ~~ would be the usual way to go, I
suppose you could also do an explicit equality check with .^/.META...

--
Mark J. Reed <mark...@mail.com>

Luke Palmer

unread,
Aug 25, 2006, 1:57:53 AM8/25/06
to Mark J. Reed, perl6-l...@perl.org
On 8/24/06, Mark J. Reed <mark...@mail.com> wrote:
> On 8/24/06, Luke Palmer <lrpa...@gmail.com> wrote:
> > Well, actually Array would be a subtype of Array::Const, not t'other
> > way round.
>
> Why? That makes no sense to me. An Array isn't a variety of
> constant Array; a constant Array is a variety of Array...

Let's say our arrays are simple, for argument's sake: With a constant
array, you can:

* get its length
* get the value of an element at an index

With an array, you can:

* get its length
* get the value of an element at an index
* set the value of an element at an index

That is, arrays have strictly more capabilites than constant arrays.
Every array can be used as a constant array, but not every constant
array can be used as an array.

You can see this recognized in C++:

int* x;
const int* y;
x = y; // illegal
y = x; // okay

Luke

Juerd

unread,
Aug 25, 2006, 4:56:03 AM8/25/06
to perl6-l...@perl.org
Luke Palmer skribis 2006-08-24 23:57 (-0600):

> Let's say our arrays are simple, for argument's sake: With a constant
> array, you can:
> * get its length
> * get the value of an element at an index
> With an array, you can:
> * get its length
> * get the value of an element at an index
> * set the value of an element at an index

You define in terms of functionality, but don't provide an explanation
for the chosen point of view.

One could say that constant arrays protect against modifications, which
normal arrays don't. Hence, constant arrays do *more*.


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

Mark J. Reed

unread,
Aug 25, 2006, 8:40:59 AM8/25/06
to Juerd, perl6-l...@perl.org
On 8/25/06, Juerd <ju...@convolution.nl> wrote:
> You define in terms of functionality, but don't provide an explanation
> for the chosen point of view. One could say that constant arrays protect against
> modifications, which normal arrays don't. Hence, constant arrays do *more*.

I think the justification for Luke's POV is the number of operations
each class provides. But my perspective agrees with Juerd -
subclasses can remove functionality as well as adding it, and I
definitely view "constant" as an add-on modifier, not a default that
has to be overridden.

Larry Wall

unread,
Aug 25, 2006, 11:23:18 AM8/25/06
to perl6-l...@perl.org

And in my view, types that remove functionality aren't called "subclasses"
but rather "subsets", and actually have the same underlying actual class,
but with different constraints.

Another view of the Array problem is that $x ~~ Array is testing
against the Array role rather than the Array class, and that the
Array class is just the default implementation of the Array role.
Array::Const is just a different implementation, like a tie.

So there are at least two ways in Perl 6 to sneak around strict Liskovism.

Larry

Trey Harris

unread,
Aug 25, 2006, 2:14:25 PM8/25/06
to Mark J. Reed, Juerd, perl6-l...@perl.org
In a message dated Fri, 25 Aug 2006, Mark J. Reed writes:
> I think the justification for Luke's POV is the number of operations
> each class provides. But my perspective agrees with Juerd -
> subclasses can remove functionality as well as adding it, and I
> definitely view "constant" as an add-on modifier, not a default that
> has to be overridden.

Can someone suggest some reading I can do to understand how that works?
I can't wrap my head around the idea of subclasses removing functionality.
Does this mean you can't write

class Super { method something { ... } }

sub foo (Super $bar) { $bar.something() }

and expect foo($obj) to work, because $obj might be:

class Sub is Super { # remove .something--how does that happen? }

foo($obj); # Boom!?

So what happens? Do you arrange somehow for polymorphism to be
interrupted, so that the call to foo() acts as though there was a
nonconformant type? Does foo() get scanned somehow to notice that
.something() is getting called, and $obj doesn't support .something, so
that bubbles up such that foo() can't be called in the first place? (If
that's true, how do you do duck-typing, since duck-typing code almost by
definition has branches that would call such invalid methods?)

I had been toying with writing some tests for the DBC blocks. But the
idea of removing functionality from a subclass completely blows away my
understanding of DBC, so I'd particularly like some reading to help me
understand how DBC works in a language that lets you remove functionality.

Trey

Trey Harris

unread,
Aug 25, 2006, 2:33:46 PM8/25/06
to jerry gay, Mark J. Reed, Juerd, perl6-l...@perl.org
In a message dated Fri, 25 Aug 2006, jerry gay writes:
> perhaps trey meant "subclasses can add constraints as well as
> functionality" instead of "subclasses can remove functionality as well
> as adding it."
>
> just a guess.
> ~jerry

Ok... same thing from a DBC perspective. Subclasses can add functionality
(by AND'ing postconditions), or remove constraints (by OR'ing
preconditions), but they can't traditionally remove functionality or add
constraints. I just want to read about how that works.

Trey

Jerry Gay

unread,
Aug 25, 2006, 2:29:55 PM8/25/06
to Trey Harris, Mark J. Reed, Juerd, perl6-l...@perl.org
On 8/25/06, Trey Harris <tr...@eecs.harvard.edu> wrote:
> In a message dated Fri, 25 Aug 2006, Mark J. Reed writes:
> > I think the justification for Luke's POV is the number of operations
> > each class provides. But my perspective agrees with Juerd -
> > subclasses can remove functionality as well as adding it, and I
> > definitely view "constant" as an add-on modifier, not a default that
> > has to be overridden.
>
> Can someone suggest some reading I can do to understand how that works?
> I can't wrap my head around the idea of subclasses removing functionality.

perhaps trey meant "subclasses can add constraints as well as

Paul Seamons

unread,
Aug 25, 2006, 2:38:30 PM8/25/06
to perl6-l...@perl.org
> Does this mean you can't write
>
> class Super { method something { ... } }
>
> sub foo (Super $bar) { $bar.something() }
>
> and expect foo($obj) to work, because $obj might be:
>
> class Sub is Super { # remove .something--how does that happen? }
>
> foo($obj); # Boom!?
>
> So what happens?

For the case in point if you tried to call @array.set(0, $element) I would
expect it to fail with an error saying you can't modify a constant array.

The method set still exists - it just politely tells you not to call that
method on that particular class.

No methods are removed.

This is very similar to read only strings.

Paul Seamons

Mark J. Reed

unread,
Aug 25, 2006, 2:37:32 PM8/25/06
to Trey Harris, Juerd, perl6-l...@perl.org
On 8/25/06, Trey Harris <tr...@eecs.harvard.edu> wrote:
> > subclasses can remove functionality as well as adding it

> Can someone suggest some reading I can do to understand how that works?


> I can't wrap my head around the idea of subclasses removing functionality.

Why not? Is it any weirder than simply changing that functionality
beyond recognition?

You can always fake removing functionality even if the language
doesn't actually support it. Consider:

class Super { method something {...} }

class Sub is Super { method something { throw new MissingMethodException; } }

or whatever that ends up looking like.

In Ruby and C++, a subclass can make a method private that is public
in its superclass, effectively removing it. (Java disallows such
shenanigans).

> Does this mean you can't write
>
> class Super { method something { ... } }
>
> sub foo (Super $bar) { $bar.something() }
>
> and expect foo($obj) to work

Of course you can expect foo($obj) to work. It might not, if the
subclass definition does something weird, but that's always the case
whether "removing functionality" is on the table or not, and it's no
reason to change your reasonable expectations. You can be paranoid
about it if you want, but it's not a very Perlish form of paranoia.
(Yes, there are Perlish forms of paranoia. Taintedness checking, for
instance...)

Trey Harris

unread,
Aug 25, 2006, 3:38:33 PM8/25/06
to Mark J. Reed, Juerd, perl6-l...@perl.org
In a message dated Fri, 25 Aug 2006, Mark J. Reed writes:

> On 8/25/06, Trey Harris <tr...@eecs.harvard.edu> wrote:
>> > subclasses can remove functionality as well as adding it
>
>> Can someone suggest some reading I can do to understand how that works?
>> I can't wrap my head around the idea of subclasses removing functionality.
>
> Why not? Is it any weirder than simply changing that functionality
> beyond recognition?

If "changing that functionality beyond recognition" means changing its
external behavior (as opposed to its internal behavior) so that it acts
differently from what the superclass had promised to do, then no, it's not
any weirder--but I can't figure out how the contract would work, either.

To extend the "contract" analogy, if I lease you a house, I may allow you
to sublease the house, and I may even agree to take my rent directly from
the sublessee rather than from you. But the sublessee can't just say to
me, "yeah, so, I'm going to pay you 1000 yen, not 1000 dollars," and
expect me to just put up with it or work around the missing money. I made
assumptions based on the original contract. If the sublessee says he wants
to pay me more or more promptly or in cash under the table, great. If he
wants to listen to opera in the evenings instead of of watching TV, that's
internal behavior having nothing to do with our contract and I don't care.

The changes have to be either in my favor or irrelevant to the contract.
Otherwise, I'm going to demand we renegotiate--in other words, that you
don't pretend to be a superclass whose behavior you're not willing to
emulate, so that when I see you I can treat you as what you are, not what
you're pretending to be.

"isa" means "is a". It doesn't mean "is roughly of a class analogous to".

> You can always fake removing functionality even if the language
> doesn't actually support it. Consider:
>
> class Super { method something {...} }
>
> class Sub is Super { method something { throw new MissingMethodException; } }
>
> or whatever that ends up looking like.
>
> In Ruby and C++, a subclass can make a method private that is public
> in its superclass, effectively removing it. (Java disallows such
> shenanigans).

But why would you want to, just because "ConstArray isa Array, not the
other way around" just feels right? "Pluto isa Planet" feels right too,
but one can't construct a reasonable definition that doesn't pull things
in that you don't want to consider Planets.

Remember, one of the goals of Perl 6 is to make it more friendly to very
large software projects. "Java disallows such shenanigans" is one example
of precisely why Java has become so popular for large projects. (The
exception-throwing example is clearly, well, an exception, and you can
always do that.)

When software gets large, you need to be able to impose some constraints
on behavior. Maybe this is a stricture or something. In any case, I'm
not trying to make a ideological argument here. I don't care if you can
remove functionality or add constraints if that helps me get my job done
(I'm just skeptical that it will).

I just want to think about the DBC features of Perl 6, and I don't know
how they could work in the context of loosening contracts in subclasses,
and I'd like somebody to point me to how, that's all. Design-by-Contract
is all about formal definitions.

>> Does this mean you can't [...] expect foo($obj) to work


>
> Of course you can expect foo($obj) to work. It might not, if the
> subclass definition does something weird, but that's always the case
> whether "removing functionality" is on the table or not, and it's no
> reason to change your reasonable expectations. You can be paranoid
> about it if you want, but it's not a very Perlish form of paranoia.
> (Yes, there are Perlish forms of paranoia. Taintedness checking, for
> instance...)

When I said "expect it to work", that was shorthand for "it *will* work,
in the absence of a bug". That's what DBC is all about--that one can
program based on the expectations of the contract, and you don't have to
write error-catching code every single time you use an object's exported
functionality.

I'll agree that DBC isn't very Perlish, in much the same way that strict
was not very Perlish in Perl 5 (for at least the first five years of its
existence, and probably even till today). But in Perl 6 strict is the
default, so by definition now Perlish. :-)

If DBC is in the language, then some people will use it, most people won't
because they don't like the inconvenience, and over time if it actually
results in more solid software, more people will choose to adopt it (like
strict).

But in order to allow that choice, the language has to impose some
groundrules for everyone. strict couldn't exist in Perl 5 if lexicals
could autovivify. And--*by my understanding of DBC*--subclasses can't
remove promised functionality or impose surprising constraints. So a)
Perl 6 can't support DBC, contrary to the Synopses, b) you can't remove
functionality or impose new constraints in subclasses, or c) my
understanding of DBC is incorrect.

Trey

Larry Wall

unread,
Aug 25, 2006, 3:52:01 PM8/25/06
to perl6-l...@perl.org
On Fri, Aug 25, 2006 at 12:38:33PM -0700, Trey Harris wrote:
: But in order to allow that choice, the language has to impose some
: groundrules for everyone. strict couldn't exist in Perl 5 if lexicals
: could autovivify. And--*by my understanding of DBC*--subclasses can't
: remove promised functionality or impose surprising constraints. So a)
: Perl 6 can't support DBC, contrary to the Synopses, b) you can't remove
: functionality or impose new constraints in subclasses, or c) my
: understanding of DBC is incorrect.

In Perl 6, option b is deemed to be correct, but allow me to reiterate that:

1) a role is not a superclass
2) a subset is not a subclass

Larry

Mark J. Reed

unread,
Aug 25, 2006, 3:53:31 PM8/25/06
to Trey Harris, Juerd, perl6-l...@perl.org
OK, I admit I wasn't thinking about things from a DBC perspective, and
misunderstood "DBC" to be a reference to some database module. I here
am new and I didn't have context. My bad.

But if we're talking design-by-contract, I don't see how "Array is
Array::Const" can work, either, since I consider the inability to
modify the elements of a constant array an explicit part of its
contract.

Therefore, if we're supporting DBC, I would say that Array and
Array::Const should not have an is-a relationship at all, but should
be sibling classes, inheriting from an abstract sequence-type parent
class whose definition makes no statements one way or the other about
modifications to elements.

Mark J. Reed

unread,
Aug 25, 2006, 3:55:48 PM8/25/06
to Trey Harris, Juerd, perl6-l...@perl.org
On 8/25/06, Mark J. Reed <mark...@mail.com> wrote:
> I here am new and I didn't have context.

Well, technically, I here am not new; I've been here since before Apoc
1. But I hadn't been paying close attention for a while until
recently. :) Either way, I didn't get the ref.

Daniel Hulme

unread,
Aug 25, 2006, 4:00:51 PM8/25/06
to perl6-l...@perl.org
> If "changing that functionality beyond recognition" means changing its
> external behavior (as opposed to its internal behavior) so that it
> acts differently from what the superclass had promised to do, then
> no, it's not any weirder--but I can't figure out how the contract
> would work, either.

That's because you're used to one way of thinking about class
inheritance: that the subclass can do everything that the superclass can
do, and more. In this scheme, you might have a Square class, with a
field representing its corner and another giving its side length. Then,
you could build on this to have Rectangle as a subclass, which adds an
extra side length, and extra accessors for it. This is a really bad way
of making your subclasses work, but your Rectangle has all the fields
and methods of your Square, and some extra ones.

Another way of looking at it is that the Rectangle is the more
generalised one, so it should be the superclass. It has a corner and two
side lengths, and associated accessors. Now, your Square is a subclass
of this. A Square in this scheme isa Rectangle, with the constraint that
the two side lengths are always equal. (Never mind the storage
considerations: that's internal.)

In the first scheme, even though the subclass only adds capabilities, it
breaks the isa relation, because a (real-world) rectangle is not
necessarily a square. If you write code that takes a Square, and you
give it a Rectangle, all the function calls will still work, but the
functionality will be wrong, because it makes assumptions that are no
longer true.

In the second scheme, the isa relation holds on the real-world things
you're abstracting, because a (real-world) square isa rectangle. But the
assumption you want to make, that you can call any superclass method on
the subclass, no longer really works. (As people said, you can hack
around it, but fundamentally it's wrong.) So, if you write code that
wants a Rectangle, and you give it a Square, you have to pussy-foot
around this externally imposed constraint, but the (smaller set of)
assumptions still hold.

Most languages use the first scheme of class inheritance, but some offer
the second. Perl 6, AFAICS, has the first for subclasses, but offers the
second with subtypes and where clauses. I don't believe I've previously
used a language that offered both, so I'm interested to see how this
conjunction of features will turn out.

--
"For God's sake, please give it up. Fear it no less than the sensual
passion, because it, too, may take up all your time and deprive you of
your health, peace of mind and happiness in life." Wolfgang Bolyai,
urging his son to give up his research on non-Euclidean geometry

Juerd

unread,
Aug 25, 2006, 4:02:35 PM8/25/06
to perl6-l...@perl.org
Trey Harris skribis 2006-08-25 11:33 (-0700):

> Ok... same thing from a DBC perspective. Subclasses can add functionality
> (by AND'ing postconditions), or remove constraints (by OR'ing
> preconditions), but they can't traditionally remove functionality or add
> constraints. I just want to read about how that works.

The keyword is "traditionally". We're used to a dynamic language that
bends the rules all the time, including runtime. Why would Perl stick to
academic limitations, while optimizing for the most common use is the
standard?

my Array::Const @foo;
@foo ~~ Array; # False?! Please, no.

Though in practice I expect "is ro" to be used, not a subtype or subset.

Trey Harris

unread,
Aug 25, 2006, 4:25:23 PM8/25/06
to Mark J. Reed, Juerd, perl6-l...@perl.org
In a message dated Fri, 25 Aug 2006, Mark J. Reed writes:

> OK, I admit I wasn't thinking about things from a DBC perspective, and
> misunderstood "DBC" to be a reference to some database module. I here
> am new and I didn't have context. My bad.
>
> But if we're talking design-by-contract, I don't see how "Array is
> Array::Const" can work, either, since I consider the inability to
> modify the elements of a constant array an explicit part of its
> contract.

Well, usually you don't specify things an object *can't* do as part of its
contract, unless those things would be judged harmful if allowed. (See
below).

You might specify the contract of the ConstArray by saying that after any
of the methods offered have been called, particular values will be
unchanged--but that feature would maintain for non-constant subclasses.
In other words, .elems could be specified at the constant superclass
level, and its contract might say that .elems won't modify the elements,
but that part of the .elems POST will maintain even in non-constant
subclasses. A rw accessor, on the other hand, would not even be specified
in the constant superclass, so the ConstArray contract would have nothing
to say about modification.

> Therefore, if we're supporting DBC, I would say that Array and
> Array::Const should not have an is-a relationship at all, but should
> be sibling classes, inheriting from an abstract sequence-type parent
> class whose definition makes no statements one way or the other about
> modifications to elements.

Sure. One *could* write a superclass, say "AbstractArrayThing", that
could not be instantiated and would have no mutating methods, and have its
PRE and POST blocks written as I mentioned above. The ConstArray would be
a subclass that would simply add a constructor. You could specify its
ongoing constedness explicitly by a class invariant block (not currently
specified in Synopses, but such a thing often exists in DBC and could be
constructed from a combo of ENTER and LEAVE at the class level) saying
that the array will still be the same array before and after every method
call.

I think that probably makes most sense and avoids the "Array isa
ConstArray" cognitive dissonance. But then people would have to get into
the habit of writing "AbstractArrayThing" when they want to allow for
constant arrays to be passed in. That way lies the madness of Java where
you're constantly writing factories to warp something so that it conforms
to the API of this other thing merely because it happens to live in the
wrong part of the class heirarchy.

I think Larry nailed it with his observation about the difference between
class and role and trait. 'Constant' is a trait of another type, not a
type into itself.

Trey

Trey Harris

unread,
Aug 25, 2006, 4:26:30 PM8/25/06
to Juerd, perl6-l...@perl.org
In a message dated Fri, 25 Aug 2006, Juerd writes:

> Trey Harris skribis 2006-08-25 11:33 (-0700):
>> Ok... same thing from a DBC perspective. Subclasses can add functionality
>> (by AND'ing postconditions), or remove constraints (by OR'ing
>> preconditions), but they can't traditionally remove functionality or add
>> constraints. I just want to read about how that works.
>
> The keyword is "traditionally". We're used to a dynamic language that
> bends the rules all the time, including runtime. Why would Perl stick to
> academic limitations, while optimizing for the most common use is the
> standard?
>
> my Array::Const @foo;
> @foo ~~ Array; # False?! Please, no.
>
> Though in practice I expect "is ro" to be used, not a subtype or subset.

Explain to me how "nontraditional" DBC might work in an internally
consistent way. Otherwise, this is hand-waving. :-)

Trey

Juerd

unread,
Aug 25, 2006, 4:18:34 PM8/25/06
to perl6-l...@perl.org
Trey Harris skribis 2006-08-25 13:26 (-0700):

> Explain to me how "nontraditional" DBC might work in an internally
> consistent way. Otherwise, this is hand-waving. :-)

Perl *is* hand-waving.

Trey Harris

unread,
Aug 25, 2006, 4:46:16 PM8/25/06
to Daniel Hulme, perl6-l...@perl.org
In a message dated Fri, 25 Aug 2006, Daniel Hulme writes:

>> If "changing that functionality beyond recognition" means changing its
>> external behavior (as opposed to its internal behavior) so that it
>> acts differently from what the superclass had promised to do, then
>> no, it's not any weirder--but I can't figure out how the contract
>> would work, either.
>
> That's because you're used to one way of thinking about class
> inheritance: that the subclass can do everything that the superclass can
> do, and more. In this scheme, you might have a Square class, with a
> field representing its corner and another giving its side length. Then,
> you could build on this to have Rectangle as a subclass, which adds an
> extra side length, and extra accessors for it. This is a really bad way
> of making your subclasses work, but your Rectangle has all the fields
> and methods of your Square, and some extra ones.

Mmmm, no... depending on usage, I'd most probably have
Shape->Polygon->Regular->Square, and Shape->Polygon->Rectangle, and write
a FourSided role (perhaps a subrole of the Sided role, or maybe Sided can
be parameterized so that Sided<4> could work) to capture the code that can
be shared between Squares and Rectangles. I'm saying that subclasses
*must* extend the promised capabilities of their parent classes, but that
extending capability shouldn't be the only determinant of when you extend
and when you make a sibling or a role or a trait.

But yes, if you didn't want to have any branching in your inheritance pole
(not tree), you'd have to make Square the superclass of Rectangle.

> Another way of looking at it is that the Rectangle is the more
> generalised one, so it should be the superclass. It has a corner and two
> side lengths, and associated accessors. Now, your Square is a subclass
> of this. A Square in this scheme isa Rectangle, with the constraint that
> the two side lengths are always equal. (Never mind the storage
> considerations: that's internal.)

The problem comes up when code tries to do this:

sub stretchWidth (Rectangle $r, Rat $ratio) {
PRE { $ratio > 0 }
$r.w *= $ratio;
POST { now($r.w) == before($r.w) * $ratio
and
now($r.h) == before($r.h)}
}

The pre and postconditions make perfect sense in the context of a
Rectangle--we need a positive stretch ratio, and only the width will be
modified, not the height. But then:

for any(@shapes) ~~ Rectangle -> $s {
stretchWidth($s, 2);
}

Again, looks perfectly reasonable--but one of @shapes was a Square, which
is also a Rectangle. Hit that one, POST fails, and boom! Either .w is
going to fail for being unmodifyable, or .w and .h are both bindings to
.side, and so stretchWidth's postcondition will fail.

> Most languages use the first scheme of class inheritance, but some offer
> the second. Perl 6, AFAICS, has the first for subclasses, but offers the
> second with subtypes and where clauses. I don't believe I've previously
> used a language that offered both, so I'm interested to see how this
> conjunction of features will turn out.

Yep. I'm excited.

Trey

Trey Harris

unread,
Aug 25, 2006, 5:04:01 PM8/25/06
to Juerd, perl6-l...@perl.org
In a message dated Fri, 25 Aug 2006, Juerd writes:

Yeah, but hand-waving on how it manages the behavior it exhibits. The
behavior itself should be defined and internally consistent. To wit, you
should be able to write a test. I can't figure out what a test for
a hypothetical nontraditional DBC would look like.

In any case, Larry's settled this issue.

Trey

Larry Wall

unread,
Aug 25, 2006, 5:10:36 PM8/25/06
to perl6-l...@perl.org
On Fri, Aug 25, 2006 at 02:04:01PM -0700, Trey Harris wrote:
: In any case, Larry's settled this issue.

Nah, I just handwaved it harder. :)

Larry

Larry Wall

unread,
Aug 25, 2006, 4:57:26 PM8/25/06
to perl6-l...@perl.org
On Fri, Aug 25, 2006 at 01:25:23PM -0700, Trey Harris wrote:
: I think Larry nailed it with his observation about the difference between
: class and role and trait. 'Constant' is a trait of another type, not a
: type into itself.

Hmm, well, there are several hands to be waved here. First, there's
more than one kind of constancy. Some types are intrinsically
immutable, some are immutable by compile-time declaration, some
are just immutable by contract, while others are operationally
constant because you never happen to call any mutators. I'd like to
distinguish the latter by calling it "readonly" rather than "constant"
or "immutable".

The big handwave about roles is that they can simultaneosly serve as
your abstract array and your concrete default implementation through
the magic of class/role punning. (It occurs to me that we probably
need to nail down the genericity (or lack thereof) of PRE and POST
blocks when attached to roles rather than classes. But in theory
roles are allowed to participate in DBC, I think.)

The big handwave about subsets is that they are basically defined
operationally rather than contractually. When I use a subset type,
the constraints are something I'm enforcing on myself, not something
enforced by the underlying base class. The actual class knows nothing
about my personal hangups. An Odd is only odd to me, not to the value,
which only knows it's an Int. You don't do DBC with subsets. You're
just being extra-disciplined outside of any contractual agreements.

That's how I see it, anyway.

Larry

Luke Palmer

unread,
Aug 25, 2006, 5:28:48 PM8/25/06
to Mark J. Reed, Trey Harris, Juerd, perl6-l...@perl.org
On 8/25/06, Mark J. Reed <mark...@mail.com> wrote:
> Why not? Is it any weirder than simply changing that functionality
> beyond recognition?
>
> You can always fake removing functionality even if the language
> doesn't actually support it.

Yes, yes, of course. That is not the issue. We are trying to
determine whether Array is a subtype of Array::Const. There are many
things you *can* do, but for core language features, it is probably
not a good idea to abandon OO foundations so quickly.

Larry says that a type that removes functionality is not a "subtype"
but a "subset". But that is bogus. Let's consider this class:

class Accessor {
has $:x;
method set($x) { $.x = $x }
method get() { $:x }
}

And let's define Accessors = the set of all objects on which you can
call set($x) and get(). That's not strictly the case, because there
may be other objects which are not Accessors which might have those
methods, but we can ignore that for the time being.

Now let's say there is another type, ConstAccessor, which removes the
set($x) ability, creating a "subset". ConstAccessors is now the set
of things on which you can call get().

And now look at the sets: Accessors = the set of all objects on which
you can call both set($x) and get(). ConstAccessors = the set of all
objects on which you can call get(). Clearly Accessors is a subset of
ConstAccessors, because anything you can call set($x) and get() on you
can call get() on.

Removing abilities, counterintuitive though it may seem on the
surface, makes the type *larger*. It is not adding constraints, it is
removing them (you might not be able to call set($x) on this object
anymore).

There are many ways to think about this. You can indeed take a
superclass which defines a method and override that method with one
that pretends the method doesn't exist, or throws some other kind of
error. But that is a pure OO no-no; you were guaranteeing from the
superclass that you could safely call a method, and then the subclass
violated that guarantee. Fortunately, Perl is not pure OO, so we are
allowed to do that in our code, but I don't think it's a good idea to
start following the faulty line of reasoning that a constant something
is a subtype of its nonconstant counterpart.

In order to resolve the linguistic conundrum of "when Array", we could
say that the Array role in fact implements a constant Array. If you
would like to be a mutable array, you implement MutableArray or
something. That would make code like this:

given $x {
when Array { $x[42] = 42; }
}

broken from a pure point of view. You may not be able to set element
42 of that array. Perl would still allow the code to compile, of
course, because Perl is a laid-back language.

Constness is something that exists, so we have to solve it somehow.
My experience with C++ tells me that the best way to solve it is to
keep it out of the way most of the time (which C++ does not). The
solution above is one way to do that, there may be others. But
pretending that const arrays are arrays with the added capability that
they throw an error when you try to write to them is going to get us
to a broken type model.

Luke

Luke Palmer

unread,
Aug 25, 2006, 6:07:48 PM8/25/06
to Daniel Hulme, perl6-l...@perl.org
On 8/25/06, Daniel Hulme <mas...@istic.org> wrote:
> That's because you're used to one way of thinking about class
> inheritance: that the subclass can do everything that the superclass can
> do, and more. In this scheme, you might have a Square class, with a
> field representing its corner and another giving its side length. Then,
> you could build on this to have Rectangle as a subclass, which adds an
> extra side length, and extra accessors for it. This is a really bad way
> of making your subclasses work, but your Rectangle has all the fields
> and methods of your Square, and some extra ones.

This is the well-known Circle/Ellipse problem, and it relates to the
theory of value types.

We'll first take the traditional conception that a Square is a
Rectangle, the opposite of your viewpoint. This is based on the
geometrical definitions of these shapes. This makes sense from a
usage standpoint:

sub rect_perimiter(Rectangle $x) {
2*$x.width + 2*$x.height;
}
sub square_perimiter(Square $x) {
4*$x.width;
}

It makes sense to pass a Square to rect_perimiter, but not to pass a
Rectangle to square_perimiter. That is one indication that Square is
a subtype of Rectangle.

However, say that Rectangle had set_height() and set_width() methods.
You cannot set the height and width of a square independently, so a
function like this:

sub make_dims(Rectangle $r, $w, $h) {
$r.set_width($w);
$r.set_height($h);
POST { $r.area == $w * $h }
}

Must fail if $w and $h are not equal (and depending on Square's
implementation, might fail if they are equal). You could say that the
failure is on the implementation side: we couldn't implement set_width
and set_height appropriately.

So clearly a Square is not a Rectangle.

Let's look at the other way around (your viewpoint): a Rectangle is a
Square. This makes sense from an implementation point of view, as you
point out. We just take Square's methods and add a couple of
capabilites.

However:

sub area(Square $x) {
$x.width ** 2;
}

Fails (by returning the wrong thing, worse than dying) if you pass it
a Rectangle. So it failed from the usage point of view. You could
say "that should be a method", but then you should say that everything
that uses a square should be a method of square, because we must be
able to make assumptions about the behavior of classes. If you say
that my sub makes an assumption that it shouldn't, realize that the
only thing it is assuming is that a Square is a geometrical square.
If I can't make that assumption, then Square is not a very good name
for that class.

So clearly a Rectangle is not a Square.

One way failed on the implementation side, the other on the usage
side. I'd argue that the "Rectangle is a Square" view's validity can
only be argued from an implementation laziness point of view (which is
okay, but it must also be balanced with other issues).

The "Square is a Rectangle" point of view only works if they are value
types: if you are not allowed to modify anything. In that
circumstance, it works very cleanly[1]. But if you are allowed to
modify things, then the two classes must be siblings, not parent-child
related.

Luke

Richard Hainsworth

unread,
Aug 28, 2006, 2:33:50 AM8/28/06
to perl6-l...@perl.org
With regard to the 'square' 'isa' 'rectangle' or vice versa question,
surely it is for the programmer to decide depending on the situation.
Though the problem is how to define criteria for trapping problems at
compile time.

Regarding classes and roles, and in fact multiple inheritance in
general, I am still unclear. For my own understanding, I have tried to
analyse various situations using the idea of sets and subsets and Venn
diagrams for demonstrating the relations between sets and subsets
(please forgive the icky character graphics).

case 1 (Class B is a subset of class A):
-----------------------------------------------------------------------------------
| Class
A |
|
-------------------------------------- |
| | Class B
| |
|
-------------------------------------- |
----------------------------------------------------------------------------------


My understanding of inheritance in other languages:
Class A 'isa' Class B, and inherits all of the attributes and
functionality of Class B, and extends functionality and attributes.

It is also possible for Class B to be ('isa') Class A, and ignore the
extra functionality of A. Though why one would want to do this is unclear.

My suggested interpretation of roles:
Class B is a role, and Class A is built from Class B, extending its
functionality.

case 2 (Class A and Class B intersect):
--------------------------------------------------------------
| Class A |
|
------------------------------------|---------------------
| | Class A U Class B | |
| |
| |
-------------------------|-----------------------------------
|
| Class
B |

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

Usual OO technique:
This Class B inherits the functionality of Class A, but then over-rides
(anuls) some of the functionality of A. Especially, if functionality in
B is named in the same way as in A.

Question: Is this the sort of behaviour that is forbidden in some languages.

Role-playing programming:
For the sake of programming sanity / ease of debugging, both Classes A &
B are built from a role that represents their intersection ( Class A U
Class B), and then code is added in the definitions of the classes to
extend the functionality - possibly using over-riding with same-name
methods/attributes for different types.

case 3 (multiple subsets):

-----------------------------------------------------------------------------------
| Class
A |
|
-------------------------------------- |
| | Class B
| |
|
-------------------------------------- |
|
-------------------------------------- |
| | Class C
| |
|
-------------------------------------- |
|
-------------------------------------- |
| | Class D
| |
|
-------------------------------------- |
----------------------------------------------------------------------------------


This would require multiple inheritance.

In perl6 (I think), Class A would be built from the 'roles' of classes B-D.

But the question is what would 'ref' return? Would it only define a
match for class A, or would it recognise the existence of Classes B-D?
If so, how?

case 4 (multiple intersecting sets - only four shown):

-----------------------------
-------------------------------------------------|------------ Class D |
| Class A | | |
|
------------------------|-----------|-----------------|--------
| |
------------|----------------- |
| --------------|-------------
| |
-----------|-------------|-------------|---------------------
|
| | | Class
B |
|
--------------|------------------------------------------------
| Class C |
----------------------------

I dont know how other OO languages handle this.

But with roles, each of the intersections could be built from roles, and
then the classes from the roles.

Regards,
Richard

Juerd

unread,
Aug 28, 2006, 5:22:24 AM8/28/06
to perl6-l...@perl.org
Richard Hainsworth skribis 2006-08-28 10:33 (+0400):

> -----------------------------------------------------------------------------------
> | Class
> A |
> |
> -------------------------------------- |
> | | Class B
> | |
> |
> -------------------------------------- |
> ----------------------------------------------------------------------------------

Your mail program is wrapping this in a way that renders it unusable.

Please make sure you use a monospaced font, and do not exceed the
wrapping limit (typically 72 characters).

> -----------------------------
> -------------------------------------------------|------------ Class D |
> | Class A | | |
> |
> ------------------------|-----------|-----------------|--------
> | |
> ------------|----------------- |
> | --------------|-------------
> | |
> -----------|-------------|-------------|---------------------
> |
> | | | Class
> B |
> |
> --------------|------------------------------------------------
> | Class C |
> ----------------------------

I'm curious what this was supposed to look like. :)

TSa

unread,
Sep 4, 2006, 12:33:23 PM9/4/06
to perl6-l...@perl.org
HaloO,

Luke Palmer wrote:
> Removing abilities, counterintuitive though it may seem on the
> surface, makes the type *larger*. It is not adding constraints, it is
> removing them (you might not be able to call set($x) on this object
> anymore).

Welcome to the co- and contra-variance problem again. We must
distinguish two sets:
(1) the set of all conceivable instances
(2) the set of constraints

Larry means (2) while Luke is talking about (1) in the
particular case of record subtyping I think. That is
methods are arrow typed slots of the object record (That is
they have type :(Object --> ::X --> ::Y)). Interestingly Perl6
doesn't provide a sound sublanguage for defining constraints
in a way that is amenable for predicate dispatch. I would
expect the where blocks to be under very strict control of the
type system but seemingly they aren't.


> In order to resolve the linguistic conundrum of "when Array", we could
> say that the Array role in fact implements a constant Array. If you
> would like to be a mutable array, you implement MutableArray or
> something. That would make code like this:
>
> given $x {
> when Array { $x[42] = 42; }
> }
>
> broken from a pure point of view. You may not be able to set element
> 42 of that array. Perl would still allow the code to compile, of
> course, because Perl is a laid-back language.

The point is that reference types are co-variant for reading and
contra-variant for writing. The only escape for rw access is mandating
type equality which in Perl6 comes as 'does Array' and has got the
rw interface.


> Constness is something that exists, so we have to solve it somehow.

Yes, but it's only half the story! The other thing that has to be
solved is writeonlyness. Both in isolation result in type soundness
in the presence of subtype directed dispatch. But both at the same
time lose this and mandate type equality.

Note that a rw Array is nicely applicable where either a readonly
or writeonly subtype is expected. The only difference is in the
return type handling, of course! Also sharing parts of the Array
implementation is orthogonal to the question of subtyping.


> But
> pretending that const arrays are arrays with the added capability that
> they throw an error when you try to write to them is going to get us
> to a broken type model.

I think type inference should be strong enough to find out that
an Array parameter of a sub is consistently used readonly or writeonly
and advertise this property accordingly to the typechecker and the
dispatcher.


Regards,
--

0 new messages