Proposal: "is defined" trait, "is typed" trait, "traits" pragma.

2 views
Skip to first unread message

Autrijus Tang

unread,
Aug 10, 2005, 12:09:46 PM8/10/05
to perl6-l...@perl.org
So I'm starting to write the inferencer. Immediately I encounter the
problem that every type can potentially contain "undef":

my IO $x = open('/etc/passwd');
$x = undef;
$x.close;

This raises the runtime error:

*** Can't call method "close" on an undefined value.

The "undef" literal can defeat the type checker freely:

my Int @x := undef;
say @x.end; # boom, undefined list generator, per S04

I understand $Larry had rejected RFC192 "Undef Values ne Value", on the
ground that "undef" can now carry more interesting information.
However, sometime it is better if we can have a clear boundary so
the dreaded NullPointerException can be raised as early as possible.

The Nice programming langugage (http://nice.sourceforge.net/) has a nice
way to solve this. Transliterating to Perl 6:

my IO $x = ...; # never undef
$x.method; # always succeeds

my ?IO $x = ...; # possibly undef
if defined $x { # needed -- direct $x.method will not typecheck
$x.method
} else { # handle the undef case
...
}

This is certainly too static for Perl6. However, it would be nice to
be at least able to declare a variable so that all undef reaching it
will instantly explode. So I propose the "is defined" trait:

# must be assigned before use
# if open() returns undef, die right there.

my IO $x is defined = open('/etc/passwd');

$x = undef; # this fails at compile time

For the compiler, $x is compiled to the real "IO" type, while the
ordinary "my IO $x" is compiled to "Either Error IO" type.

Furthermore, it would be nice to tell the inferencer to infer the type
of a certain variable, instead of having to either write them out by
hand, or accepting their fate as Any. I'd like to see "is typed":

{
my $x is typed; # Infer $x's type (Str) for me
$x = "Hello";
print $x;
}

Finally, it would get tedious to write them out by hand. So a lexical
"traits" pragma may help:

{
# Entering the realm of referential transparency...
use traits < defined typed constant >;
my $x; # automagically receives the three traits

{
# Falls back to the dynamic world...
no traits < typed constant >;
my $y;
}
}

Does this sound sane?

Thanks,
/Autrijus/

Autrijus Tang

unread,
Aug 10, 2005, 1:34:34 PM8/10/05
to perl6-l...@perl.org
On Wed, Aug 10, 2005 at 10:25:05AM -0700, Larry Wall wrote:
> I'll have to think about the rest of your proposal, but I was suddenly
> struck with the thought that our "platonic" Class objects are really
> forms of undef:
>
> say defined IO; # prints 0

Hmm, bool::false stringifies to '0'?

Also, isn't IO an instance of Class, and hence defined?

My current understanding is that the typechecker considers IO to be of
Class type, not of IO type; the fact that IO.does(IO) is true is purely
an illusion created by special dispatch for .does.

Am I way off base? :)

Thanks,
/Autrijus/

Larry Wall

unread,
Aug 10, 2005, 1:25:05 PM8/10/05
to perl6-l...@perl.org
I'll have to think about the rest of your proposal, but I was suddenly
struck with the thought that our "platonic" Class objects are really
forms of undef:

say defined IO; # prints 0

That is, we already have an object of type IO that doesn't really
have a value yet. And maybe that's the real difference between the
class object and the metaclass object. And maybe we can reason
about objects of type IO without worrying about the definedness,
if all classes already include their own personal undef.

Larry

Autrijus Tang

unread,
Aug 10, 2005, 2:40:40 PM8/10/05
to perl6-l...@perl.org
On Wed, Aug 10, 2005 at 11:24:05AM -0700, Larry Wall wrote:
> : My current understanding is that the typechecker considers IO to be of

> : Class type, not of IO type; the fact that IO.does(IO) is true is purely
> : an illusion created by special dispatch for .does.
>
> Well, that's what I thought last week. :-)
>
> But these days I'm wondering if the whole point of a class is to proxy
> for its missing members, and everything else is deferred to the metaclass.

Hm. How is this different from prototype-based OO, as in JavaScript?
I'd like to see some code examples that shows the difference of the
week-before view and the current view...

Thanks,
/Autrijus/

Larry Wall

unread,
Aug 10, 2005, 2:24:05 PM8/10/05
to perl6-l...@perl.org
On Thu, Aug 11, 2005 at 01:34:34AM +0800, Autrijus Tang wrote:

: On Wed, Aug 10, 2005 at 10:25:05AM -0700, Larry Wall wrote:
: > I'll have to think about the rest of your proposal, but I was suddenly
: > struck with the thought that our "platonic" Class objects are really
: > forms of undef:
: >
: > say defined IO; # prints 0
:
: Hmm, bool::false stringifies to '0'?

Well, okay, maybe I meant:

say +defined IO; # prints 0

: Also, isn't IO an instance of Class, and hence defined?

It's up to the instance to tell you whether it's defined or undefined.
Perl 5 guarantees that all refs are defined, but Perl 6 makes no
such guarantee. The whole interesting-undef thing depends on this.

: My current understanding is that the typechecker considers IO to be of


: Class type, not of IO type; the fact that IO.does(IO) is true is purely
: an illusion created by special dispatch for .does.

Well, that's what I thought last week. :-)

But these days I'm wondering if the whole point of a class is to proxy
for its missing members, and everything else is deferred to the metaclass.

: Am I way off base? :)

Maybe, but it doesn't matter if your teammate just knocked a home run.
Unfortunately the ball is still in the air, and we don't know if it'll
clear the fence, or land in someone's mit.

Larry

TSa

unread,
Aug 10, 2005, 3:29:38 PM8/10/05
to Autrijus Tang, perl6-l...@perl.org
HaloO,

Autrijus Tang wrote:
> So I'm starting to write the inferencer. Immediately I encounter the
> problem that every type can potentially contain "undef":

This is the reason why I've bounded Undef below Item disjoint with
Value in my type lattice. Of course all types are applicable as
type parameters. How implicit that is or if an explicit

class IO does Undef[IO] {...}

is needed, I can't decide. And I just realised today that decisions
are made in places and at times where I am not. And they are not
announced here either. But that belongs to the cabal thread. Sorry
that I mention it here.


> my IO $x = open('/etc/passwd');
> $x = undef;

Hmm, that should dispatch to &infix{'='}:( IO, Undef of IO )
which is a type error if Undef of IO doesn't exist or pragmas
or some such prevent its auto-generation. A nice implementor
of IO would be so kind to close the file immediately in this
method.


> $x.close;
>
> This raises the runtime error:
>
> *** Can't call method "close" on an undefined value.

The dispatch should go to &close:( Undef of IO --> Void ) and again
the nice implementor of IO.does(Undef of IO) would provide a noop
implementation. A careless implementor however might leave his
users with the above error ;)


> The "undef" literal can defeat the type checker freely:
>
> my Int @x := undef;
> say @x.end; # boom, undefined list generator, per S04
>
> I understand $Larry had rejected RFC192 "Undef Values ne Value", on the
> ground that "undef" can now carry more interesting information.

Yes, but I think this should now be read as !Value.does(Undef) and
Item.does(Undef).

> However, sometime it is better if we can have a clear boundary so
> the dreaded NullPointerException can be raised as early as possible.

I fully agree. Especially when it hits innocent users.


> This is certainly too static for Perl6. However, it would be nice to
> be at least able to declare a variable so that all undef reaching it
> will instantly explode. So I propose the "is defined" trait:

I would leave that up to the type information stored at ::IO.
The user has to know what it does anyhow. And if it is a Value
subtype it'll instantly explode even on

my IO $x; # boom unless IO provides default value, but how?

If the inclined user intends to handle Undef even when
the implementor of IO didn't this is how:

my IO|Undef[IO] $x; # proper supertype
my IO^Undef[IO] $y; # decisive, this is easier for the type checker

> # must be assigned before use
> # if open() returns undef, die right there.

Even worse or better depending on your perspective or stricture
in effect. If IO.does(Value) and &open:( Str --> IO^Undef )
then

> my IO $x is defined = open('/etc/passwd');

Doesn't even compile. Not so if you take the Widera approach
where the intersection of the return type of &open and the
type constraint of $x is non-empty because it contains IO.
But if your approach it the other way round, the potential error
set isn't empty either. This is what I got from the
(Book of Widera).does(Paper) so far. Well, and the fact that
he calls things Frame that are elsewhere known as Record or
Dictionary.

OTOH, the implementor of &open could choose to guarantee
a return type of IO given an arbitrary Str. In the event
that there is no file of that name the returned object
is a proper IO but e.g. immediately returns EOF when read
or responds with false to .defined calls. But this is not
a problem of the type system but of interface design. The
type system is there to enforce the designer's decisions
or at least point out mismatches to actual use.


> $x = undef; # this fails at compile time
>
> For the compiler, $x is compiled to the real "IO" type, while the
> ordinary "my IO $x" is compiled to "Either Error IO" type.
>
> Furthermore, it would be nice to tell the inferencer to infer the type
> of a certain variable, instead of having to either write them out by
> hand, or accepting their fate as Any. I'd like to see "is typed":

I don't understand the purpose of that other than explicitly switching
the inferencer on a per variable basis. Should that mean that a variable
takes on a constraint as specific as the inferencer can statically
determine? I don't see the point of it, sorry.


> {
> my $x is typed; # Infer $x's type (Str) for me
> $x = "Hello";
> print $x;
> }

The optimizer might here just end up with { print "Hello"; }
and drop $x completely. Or is that anti-social to some
class sitting behind $-sigiled variables?


> Finally, it would get tedious to write them out by hand. So a lexical
> "traits" pragma may help:
>
> {
> # Entering the realm of referential transparency...
> use traits < defined typed constant >;
> my $x; # automagically receives the three traits
>
> {
> # Falls back to the dynamic world...
> no traits < typed constant >;
> my $y;
> }
> }
>
> Does this sound sane?

To me it sounds more superfluous. What distinguishes
'referential transparency' from 'the dynamic world'?
Do you mean pre-calculated dispatch targets?
--
$TSa.greeting := "HaloO"; # mind the echo!

Autrijus Tang

unread,
Aug 10, 2005, 3:38:05 PM8/10/05
to TSa, Autrijus Tang, perl6-l...@perl.org
On Wed, Aug 10, 2005 at 09:29:38PM +0200, TSa wrote:
> >Finally, it would get tedious to write them out by hand. So a lexical
> >"traits" pragma may help:
> >
> > {
> > # Entering the realm of referential transparency...
> > use traits < defined typed constant >;
> > my $x; # automagically receives the three traits
> >
> > {
> > # Falls back to the dynamic world...
> > no traits < typed constant >;
> > my $y;
> > }
> > }
> >
> >Does this sound sane?
>
> To me it sounds more superfluous. What distinguishes
> 'referential transparency' from 'the dynamic world'?

I think that's because you live in the static realm already. :)

my $x is typed;
$x = "123";
$x = length($x);

Would be a type error. If it's in the dynamic world (as in Perl5),
that's just fine. Does that difference make sense to you?

Thanks,
/Autrijus/

TSa

unread,
Aug 10, 2005, 4:31:40 PM8/10/05
to perl6-l...@perl.org
HaloO Autrijus,

you wrote:
> I think that's because you live in the static realm already. :)

Perhaps, but if yes it is an infinite one.


> my $x is typed;
> $x = "123";
> $x = length($x);
>
> Would be a type error. If it's in the dynamic world (as in Perl5),
> that's just fine. Does that difference make sense to you?

The question remains, when exactly does $x acquire its constraint
and how long does it persist? For my vars it's easy to say: "from
first assignment of none(Undef).does(Item) to end of scope".
Which in the above code gives: the first assignment puts a Str
into $x and if &length returns an Int the second assignment fails.

This is what I understand. But how usefull is this feature?
I mean the programmer who reads 'my Int $x' knows that everything
that is true for an Int is true wherever $x is subsequently used.
A 'my $x is typed' pretty much means: "read on and find out what
type $x aquires".

Do you have a non-trivial application for this in mind? It reminds
me a bit to the 'bind a variable once in the constraint store' of
the constraint programming paradigma. But there it serves as guiding
the control flow. What is the purpose in a typed, imperative language
as Perl6?

Autrijus Tang

unread,
Aug 10, 2005, 10:47:35 PM8/10/05
to TSa, perl6-l...@perl.org
On Wed, Aug 10, 2005 at 10:31:40PM +0200, TSa wrote:
> > my $x is typed;
> > $x = "123";
> > $x = length($x);
> >
> >Would be a type error. If it's in the dynamic world (as in Perl5),
> >that's just fine. Does that difference make sense to you?
>
> The question remains, when exactly does $x acquire its constraint
> and how long does it persist? For my vars it's easy to say: "from
> first assignment of none(Undef).does(Item) to end of scope".
> Which in the above code gives: the first assignment puts a Str
> into $x and if &length returns an Int the second assignment fails.

Hm, in a language with type inference, the process is more like
inventing an anonymous type variable (let's call it ::X for now) for $x,
and noting the three constraints:

Str.does(::X) # store from "123"
::X.does(Str) # fetch into length()
Int.does(::X) # store from length()

No monotype satisfies the constraints, so unification fails and an
exception is raised.

> This is what I understand. But how usefull is this feature?
> I mean the programmer who reads 'my Int $x' knows that everything
> that is true for an Int is true wherever $x is subsequently used.
> A 'my $x is typed' pretty much means: "read on and find out what
> type $x aquires".

Yes, but without this, you have to write down the full type every time
you declare a variable in order to get any typechecking at all. In many
cases the compiler already knows it for you.

Really this is about path of least resistance. Without inference,
we are asking the user to choose between:

1) Verbose annotation and type safety
2) Convenience (no annotation) and unsafe behaviour

Adding inference ("is typed") to the mix massively sweetens the first
option, which would be a good thing.

> Do you have a non-trivial application for this in mind?

Yes. But I can only show trivial examples:

use traits <typed>;
my $test = Test::Builder.new();

Now, without inferencing (the first line above), $Test would have to be
dynamically typed (no typechecks possible), essentially forcing the user
who wants typechecks to write out by hand:

my Test::Builder $test = Test::Builder.new();

This is silly. Or, take another trivial (but common) example:

method skip (--> Test::Builder::Test::Skip) {
my Test::Builder::Test::Skip $skip = Test::Builder::Test::Skip.new;
my Test::Builder::Test::Base::Status $status = $skip.status;
$status.do_something; $skip.do_something; # ...
return $skip;
}

With inferencing:

use traits 'typed';
method skip (--> Test::Builder::Test::Skip) {
my $skip .= new;
my $status = $skip.status;
$status.do_something; $skip.do_something; # ...
return $skip;
}

Which language do you want to write in? :-)

> It reminds me a bit to the 'bind a variable once in the constraint
> store' of the constraint programming paradigma.

No, I didn't have that in mind; I think it's just local type inferences.

> But there it serves as guiding the control flow. What is the purpose
> in a typed, imperative language as Perl6?

The purpose is that we don't have to be strong typists to enjoy Strong
Typing. To make Perl6 easier to type, and easier to Type.

Thanks
,/Autrijus/

TSa

unread,
Aug 11, 2005, 2:59:51 AM8/11/05
to Autrijus Tang, perl6-l...@perl.org
HaloO,

Autrijus Tang wrote:
> The purpose is that we don't have to be strong typists to enjoy Strong
> Typing. To make Perl6 easier to type, and easier to Type.

Great! You, if not solve, but at least aim at relieving the pain caused
by the 'proliferation of type parameters' problem. Consider me as convinced.

Stuart Cook

unread,
Aug 11, 2005, 6:17:14 AM8/11/05
to perl6-l...@perl.org

Wow, that's crazy enough to actually work!

This approach has a few nice properties:

* It neatly handles the fact that 'platonic' instances only kinda-sorta
exist by making them undefined. You can answer hypothetical questions
about instances in general, but as soon as you try to treat undef
as an actual instance of something (rather than just dispatching to
the class) you'll encounter an error.

* Classes don't have to be considered first-class objects (beyond being
the type of certain special undefs); they can be their own kind of
thing, and if you want to treat them as objects you can just use the
metaclass.

* It puts up quite a clear barrier between the responsibilities of the
class (dispatch class methods and initialisers, but don't actually
/do/ anything) and the metaclass (act as the class's representative
in object-space).

Am I thinking along the same lines as you?


Stuart

Brent 'Dax' Royal-Gordon

unread,
Aug 11, 2005, 6:26:19 AM8/11/05
to Stuart Cook, perl6-l...@perl.org
Stuart Cook <sco...@gmail.com> wrote:
> On 11/08/05, Larry Wall <la...@wall.org> wrote:
> > I'll have to think about the rest of your proposal, but I was suddenly
> > struck with the thought that our "platonic" Class objects are really
> > forms of undef:
> >
> > say defined IO; # prints 0
> >
> > That is, we already have an object of type IO that doesn't really
> > have a value yet. And maybe that's the real difference between the
> > class object and the metaclass object. And maybe we can reason
> > about objects of type IO without worrying about the definedness,
> > if all classes already include their own personal undef.
>
> Wow, that's crazy enough to actually work!
>
> This approach has a few nice properties:

One that you missed was that this syntax:

my Dog $spot .=new();

Falls out of it quite naturally.

On the other hand, there are other things that don't work quite so well:

my Dog $spot;
$spot.can('bark'); # Not until he's instantiated...

On the gripping hand, maybe you should have to ask the metaclass about
that anyway:

$spot.meta.class_can('bark'); #No
$spot.meta.instance_can('bark'); #Yes

Hmm.

--
Brent 'Dax' Royal-Gordon <br...@brentdax.com>
Perl and Parrot hacker

Stuart Cook

unread,
Aug 11, 2005, 6:53:47 AM8/11/05
to perl6-l...@perl.org
On 11/08/05, Brent 'Dax' Royal-Gordon <bren...@gmail.com> wrote:
> One that you missed was that this syntax:
>
> my Dog $spot .=new();
>
> Falls out of it quite naturally.

Actually I tried to mention that indirectly, but I'm glad you
explicitly mentioned it.

> On the other hand, there are other things that don't work quite so well:
>
> my Dog $spot;
> $spot.can('bark'); # Not until he's instantiated...

Are you objecting to the fact that it can't possibly return a valid
method, or that it will inappropriately true/false (depending on your
point of view)?

> On the gripping hand, maybe you should have to ask the metaclass about
> that anyway:
>
> $spot.meta.class_can('bark'); #No
> $spot.meta.instance_can('bark'); #Yes

Yeah, but if we're trying to view (undef but Dog) as the platonic
instance of Dog, it would be nice if told us what the ideal Dog can &
can't do. (In either case, the metaclass will be able to tell us.)
Something to ponder, I suppose.


Stuart

Autrijus Tang

unread,
Aug 11, 2005, 10:13:11 AM8/11/05
to Stuart Cook, perl6-l...@perl.org
On Thu, Aug 11, 2005 at 08:53:47PM +1000, Stuart Cook wrote:
> On 11/08/05, Brent 'Dax' Royal-Gordon <bren...@gmail.com> wrote:
> > One that you missed was that this syntax:
> >
> > my Dog $spot .=new();
> >
> > Falls out of it quite naturally.
>
> Actually I tried to mention that indirectly, but I'm glad you
> explicitly mentioned it.

What about this?

my $spot = Dog;
defined($spot); # false!?
$spot .= new; # instantiated?

Thanks,
/Autrijus/

TSa

unread,
Aug 11, 2005, 10:47:49 AM8/11/05
to perl6-l...@perl.org
HaloO,

Autrijus Tang wrote:
> What about this?

OK, let's play some manual type inferencing ;)


> my $spot = Dog;

$spot.does(Item of Dog), that means what ever the name Dog represents
was stored or bound to $spot.

> defined($spot); # false!?

true! Even for my $spot = ::Dog because when my is evaluated the
name ::Dog has be be bound, AUTOLOADED or by whatever means become
available.

> $spot .= new; # instantiated?

Why not, a bit weird though to replace the class link with
a fresh instance of same class.

Autrijus Tang

unread,
Aug 11, 2005, 11:00:36 AM8/11/05
to TSa, perl6-l...@perl.org
On Thu, Aug 11, 2005 at 04:47:49PM +0200, TSa wrote:
> OK, let's play some manual type inferencing ;)

Note that $spot here is intended to be dynamic typed, i.e. not subject
to inference. :-)

> > my $spot = Dog;
>
> $spot.does(Item of Dog), that means what ever the name Dog represents
> was stored or bound to $spot.

Right, except $spot can later hold other non-Dog values as well, as it
is dynamically typed by default...

> > defined($spot); # false!?
>
> true! Even for my $spot = ::Dog because when my is evaluated the
> name ::Dog has be be bound, AUTOLOADED or by whatever means become
> available.

I agree, with exactly the same logic. However, Larry somehow thought
that defined(Dog) is false, which should lead defined($spot) is false
as well. I still can't quite grasp this idea. :)

Thanks,
/Autrijus/

Larry Wall

unread,
Aug 11, 2005, 12:44:06 PM8/11/05
to perl6-l...@perl.org
On Thu, Aug 11, 2005 at 04:47:49PM +0200, TSa wrote:
: > defined($spot); # false!?

:
: true! Even for my $spot = ::Dog because when my is evaluated the
: name ::Dog has be be bound, AUTOLOADED or by whatever means become
: available.

What does binding have to do with definedness? In Perl 6 the object
itself decides if it's defined regardless of how or where it's bound.
That's how we get interesting values of undef. The recent proposal
only tweaks that idea to unify typed undefs with interesting undefs
so that an unthrown exception can also be an abstract (albeit
unsuccessful) IO or Dog or whatever, so that you don't have to play
games with junctions to get undef into typed variables. In other
words, $spot is not successful, but that can be either because we
tried and failed, or because we haven't tried yet. That's the
unification I'm looking at.

Larry

TSa

unread,
Aug 11, 2005, 12:16:09 PM8/11/05
to Stuart Cook, perl6-l...@perl.org
HaloO,

Stuart Cook wrote:
>>On the other hand, there are other things that don't work quite so well:
>>
>> my Dog $spot;
>> $spot.can('bark'); # Not until he's instantiated...
>
>
> Are you objecting to the fact that it can't possibly return a valid
> method, or that it will inappropriately true/false (depending on your
> point of view)?

Might I add my view of affairs? I want to point out the difference
between a slot call and a free method here. Major difference is that
a slot call starts from the $spot sigil expression, determines the
class and looks up .can there and calls it. While a free method .can
is lookup up first in namespace and then $spot has to qualify as an
invocant type. The selected dispatch target has to accept a Str
parameter and is executed in Void context.


> Yeah, but if we're trying to view (undef but Dog) as the platonic
> instance of Dog,

$Larry very nicely said that the name Dog here represents all
the potentialities with the package/module/class behind it.
I regard this potentiality as type information. That is after
my Dog $spot; the type of $spot is 'Item of Undef of Dog'.

> it would be nice if told us what the ideal Dog can &
> can't do. (In either case, the metaclass will be able to tell us.)
> Something to ponder, I suppose.

Proposal: could we call all this META stuff somewhat generically
"Meta Info"? I mean .meta delivers a thingy that .does(Meta) nothing
more nothing less. There are whatever methods defined on that type.
Does that help you pondering?

In more implementation oriented terms .meta returns an opaque pointer
from which other methods can deliver whatever stringy, objectish or
classish information, links or refs. I guess parts of the structures
that this opaque pointer points to are really located on the Parrot
C level or equivalent things in other interpreters supporting Perl6.

Larry Wall

unread,
Aug 11, 2005, 12:22:27 PM8/11/05
to perl6-l...@perl.org
On Thu, Aug 11, 2005 at 10:47:35AM +0800, Autrijus Tang wrote:
: Really this is about path of least resistance. Without inference,

: we are asking the user to choose between:
:
: 1) Verbose annotation and type safety
: 2) Convenience (no annotation) and unsafe behaviour
:
: Adding inference ("is typed") to the mix massively sweetens the first
: option, which would be a good thing.

Only if the programmer can be taught to understand the inferences...

: > Do you have a non-trivial application for this in mind?


:
: Yes. But I can only show trivial examples:
:
: use traits <typed>;
: my $test = Test::Builder.new();
:
: Now, without inferencing (the first line above), $Test would have to be
: dynamically typed (no typechecks possible), essentially forcing the user
: who wants typechecks to write out by hand:
:
: my Test::Builder $test = Test::Builder.new();

That's why we have:

my Test::Builder $test .= new();

: This is silly. Or, take another trivial (but common) example:


:
: method skip (--> Test::Builder::Test::Skip) {
: my Test::Builder::Test::Skip $skip = Test::Builder::Test::Skip.new;
: my Test::Builder::Test::Base::Status $status = $skip.status;
: $status.do_something; $skip.do_something; # ...
: return $skip;

: }

my Test::Builder::Test::Skip $skip .= new;

: With inferencing:


:
: use traits 'typed';
: method skip (--> Test::Builder::Test::Skip) {
: my $skip .= new;
: my $status = $skip.status;
: $status.do_something; $skip.do_something; # ...
: return $skip;
: }
:
: Which language do you want to write in? :-)

Probably the first, actually, given the .= shortcut. It takes *me*
a lot of thought to reproduce in my head what the inferencer is doing
there, and you can't really understand code unless you can play the
same mindgames the computer is playing. When the computer gets too
smart, it forces anyone of less-than-genius caliber into cargo-cult
programming. That's what I don't like about type inferencing systems.

And there's something to be said for locating type information near
the "my" declaration for documentation purposes even if it's entirely
redundant.

: > It reminds me a bit to the 'bind a variable once in the constraint


: > store' of the constraint programming paradigma.
:
: No, I didn't have that in mind; I think it's just local type inferences.
:
: > But there it serves as guiding the control flow. What is the purpose
: > in a typed, imperative language as Perl6?
:
: The purpose is that we don't have to be strong typists to enjoy Strong
: Typing. To make Perl6 easier to type, and easier to Type.

Sure, but you're kinda smart, so you can just remember what you've
typed and/or Typed. Stupid people also have to worry about reading
the code. :-)

Actually, this is a case where a declared return variable would reduce
my cognitive load since I don't have to scan for a return statement
to see that $skip is in fact the return value:

use traits 'typed';
method skip (--> Test::Builder::Test::Skip $skip) {


$skip .= new;
my $status = $skip.status;
$status.do_something; $skip.do_something; # ...
}

Arguably that's just locating the type near the "my" again, though the
"my" is implicit in the signature in this case.

Larry

Autrijus Tang

unread,
Aug 11, 2005, 12:54:12 PM8/11/05
to perl6-l...@perl.org
On Thu, Aug 11, 2005 at 09:22:27AM -0700, Larry Wall wrote:
> On Thu, Aug 11, 2005 at 10:47:35AM +0800, Autrijus Tang wrote:
> : Adding inference ("is typed") to the mix massively sweetens the first
> : option, which would be a good thing.
>
> Only if the programmer can be taught to understand the inferences...

Aye. That is a very good argument for "is typed" to be off by default,
which I fully concur. :)

> Probably the first, actually, given the .= shortcut. It takes *me*
> a lot of thought to reproduce in my head what the inferencer is doing
> there, and you can't really understand code unless you can play the
> same mindgames the computer is playing. When the computer gets too
> smart, it forces anyone of less-than-genius caliber into cargo-cult
> programming. That's what I don't like about type inferencing systems.

Indeed, but I expect Perl6 programmers start rapid-prodotyping in the
dynamic variant of Perl6, so the inferencing does not kick in anyway.

However, once a programmer starts using type annotations, rapidly the
compiler will (rightfully) complain about unsafe coercing. That would
force the programmer into another kind of cargo-cult programming,
rampant in the Java world, namely manually key-in type declarations
everywhere.

Also, it is common in inference-based language environment to tell you
the inferred type for expressions, in a way that can be copy-and-pasted
back to the program, instead of having to type them out by hand.

Something like this:

pugs> sub fact ($x) is typed { [*] 1..$x }
pugs> :t &fact
sub fact (Int $x --> Int)

> And there's something to be said for locating type information near
> the "my" declaration for documentation purposes even if it's entirely
> redundant.

That is true.

> Actually, this is a case where a declared return variable would reduce
> my cognitive load since I don't have to scan for a return statement
> to see that $skip is in fact the return value:
>
> use traits 'typed';
> method skip (--> Test::Builder::Test::Skip $skip) {
> $skip .= new;
> my $status = $skip.status;
> $status.do_something; $skip.do_something; # ...
> }
>
> Arguably that's just locating the type near the "my" again, though the
> "my" is implicit in the signature in this case.

Yes, I think that form would rock. Also, I think leaving off the
Test::Builder::Test::Base::Status from the $status declaration does
not harm its documentation value -- indeed, that's the DWIM form Perl
programmers are used to.

Thanks,
/Autrijus/

TSa

unread,
Aug 12, 2005, 6:46:50 AM8/12/05
to perl6-l...@perl.org
HaloO,

Larry Wall wrote:
> On Thu, Aug 11, 2005 at 04:47:49PM +0200, TSa wrote:
> : > defined($spot); # false!?
> :
> : true! Even for my $spot = ::Dog because when my is evaluated the
> : name ::Dog has be be bound, AUTOLOADED or by whatever means become
> : available.
>
> What does binding have to do with definedness?

We seem to struggle in around words which can be deceiving.
This is why I used 'whatever'. Before some meaning is attached
to them the three strings '$spot', '=' and '::Dog' are just
three sequences of characters separated by whitespace.

Since the moment when meaning is put behind '::Dog' is somewhat
vague, I tried to preserve that vagueness in my sentence. And
it seems to be the case that '::Dog' is more vague than 'Dog'.
I don't care of how many people's code, modules, packages,
AUTOLOADERS or whatever has to meddle before the meaning behind
::Dog becomes available. Could we please agree on a term that
denotes the fact that ::Dog leads somewhere. Seemingly my
'name ::Dog ... bound' wasn't clear enough.


> In Perl 6 the object
> itself decides if it's defined regardless of how or where it's bound.

I wasn't talking about an object at all. The string 'defined($spot)'
once again needs a meaning attached to 'defined' and '$spot' the
parens are prescribed by the grammar. And so is the fact that 'defined'
has to be a Sub subtype that can handle the type of $spot. I guess we
all agree that the least type of &defined is :( Item --> Bool) and
$spot has at most that type by virtue of the $ sigil. So we know that
the type of 'defined($spot)' is Bool. The value at runtime is irrelevant
in that contemplation. The question is can we do better and conclude
a subtype of Bool, that is either the type true or the type false,
not the runtime values! This knowledge can hardly come from the return
value of &defined. Thus it must be derived from more specific type
information about $spot. If that is not available, the story ends here.

<interjection>
The above invocation of &defined happens in Void context
but I suppose that &defined is not (declared|defined|specified|whatever)
to provide that type. Could that be optimized to not generating
a call at all? How would such a feature be declared? Are there
'is eager' and 'is lazy' traits for subs and methods? And which is
the default?
</interjection>


> That's how we get interesting values of undef. The recent proposal
> only tweaks that idea to unify typed undefs with interesting undefs
> so that an unthrown exception can also be an abstract (albeit
> unsuccessful) IO or Dog or whatever, so that you don't have to play
> games with junctions to get undef into typed variables.

Sorry, I can't follow. What is a 'typed variable' to you? How does
it relate to 'typed undef'. I have put Undef as a subtype under Item,
next to Value, Ref and Junction. This means to me that if you know
of a certain expression that it is typed as Value then Undef is
excluded. If all you know is that you have an Item then Undef is
also included as is Ref or Junction. This is how type systems work.

For example the string 'my @a' introduces an Array by virtue of the
single char @. The expression '@a[23]' is already a quite complicated
thing. It's an invocation of the index operator. The type of which
is :( Array, Int --> Item). And with the Item our friend Undef enters
the picture. Consider

my Int @a;
my Int $i;

$i = @a[23]; # dispatch to op = :(Undef of Int, Pos[Array])
# note that (Undef of Int).does(Int) hypothetically
@a[23] = 42;

say $i; # prints 42?

A nice implementation of op = could store something in $i that
somehow finds back to slot 23 of @a. My idea of such a type is
Pos[Array of Int] which is a subtype of Int through two paths.
And of Ref[Int], but that is not important here directly.
Thus its value can be interpreted as any(23,42) with a
preference for 42 because that is what the user might expect.
Please note that this behaviour is implemented by providing
a certain target for op = dispatches, *not* through messing with
the Array, Item or Int classes! If @a[23] returns an Int
eagerly then the dispatch goes to :(Undef of Int, Int) and
something else happens. But this decision of return type again
needs different targets for the index operator *not* the variables,
containers or classes!

> In other
> words, $spot is not successful, but that can be either because we
> tried and failed, or because we haven't tried yet. That's the
> unification I'm looking at.

What would you expect from

delete(@a[23]); # make slot 23 in @a undefined again

say $i;

The type system is quite happy with the integer value 23.
But the Pos type could be made to complain that it's link
to the 42 is cut if that is what you want.

BTW, is the distinction between Entry of Hash and Pos of Array
usefull or should that be unified under Elem of Hash|Array|Str?
But then again the pairs of a hash are not retrieved as List of Pair
with the .elems method which retrieves a List of Item from an Array,
or are they? Actually I fear that I have the wrong attitude here
and suspect the answer: "But .pairs is inside the Hash class while
.elems is inside the Array class." The question is if you start
from the Array class and then reason about the methods it has or
if you start from the .elem method---which is an instance of the
Method class---and reason how it behaves given different types of
invocants.

Blue Sky question: is it thinkable to fold slot calls into the
pair syntax as follows?

my $obj = SomeClassWithSlot_foo.new;

say $obj:foo; # prints return value of slot call foo

I mean the adverbial modifier syntax already works on Code objects.
That is

sub blahh( +$foo ) {...}

$obj = &blahh;

already attaches a meaning to

say $obj:foo;

This time the return value of blahh is printed. I guess blahh receives
a $foo = 0 but true. This might also give the shortest form of hash
access proposed so far:

%hash:key = 23; # %hash<key> = 23; is one char longer ;)

and

%hash :key = 23; # %hash .<key> = 23; is two chars longer ;))

Even better, the syntax

say:loud:margin(8):tabs(4) foo();

can be seen as currying &say one named parameter at a time
from left to right. The result is a sub that receives the
return value of the foo() invocation for printing. Since I'm
at it...

&mysay = &say:loud:margin(8):tabs(4); # currying slot calls

might just be a short form of .assuming and then

mysay foo();

is the same as above. And of course also

&map_add = &map:{$^x + $^y};

say map_add 1,2,3,4,5,6;

But of course I'm not the language designer---and much
less the Lord of the Colon.

The above in a certain way increases the number of sigils
to my favorite 'The 7 Endless':

::Meta

&Sub
.Method :Slot

:Pair %Hash
$Item @Array

First comes unavoidably the typing Destiny, then his brother
and sister Dream and Death, then the data part of the Family:
Destruction, Desire, Despair and Delirium.

Besides, seven is a very beautiful number. E.g.

7 == 2**0 + 2**1 + 2**2 == 1 + 2 + 4

The sum of Two taken to the power of the three levels of the type system.
Level[-1]: the underlying VM
Level [0]: pure abstraction, but standard Perl6 pre-loaded
Level [1]: closure introduction
Level [2]: fundamental types to build stuff from,
that is given to closures to do something with it

Here is my mapping:

::Meta --> Destiny, the oldest all knowing, blind wanderer

&Sub --> Dream, Morpheus, the Sandman
.Method --> Death, dealing on invocants

:Pair --> Destruction, left his realm and actually constructs things
%Hash --> Delirium, somewhat puzzled err hashed
@Array --> Desire, always wanting more content (sigil is a heart)
$Item --> Despair, when nothing else works (her sigil is a hook)

Your pick?

TSa

unread,
Aug 12, 2005, 10:53:13 AM8/12/05
to perl6-l...@perl.org
TSa wrote:
> :Pair %Hash
> $Item @Array

Here I forget to mention the beautiful symmetry:

| arity
access | 1 | 0..Inf
-----------+-------+---------
keyed | :Pair | %Hash
positional | $Item | @Array

Regards,

Reply all
Reply to author
Forward
0 new messages