When Perl sees:
class Joe { my $.a; method b {...} }
my Joe $j;
Many things happen and some of them will require knowing what the result
of the previous thing is.
More to the point, Perl 6's compiler will have to parse "class Joe",
create a new object of type Class, parse and execute the following
block/closure in class MetaClass, assign the result into the new Class
object named Joe and then continue parsing, needing access to the values
that were just created in order to further parse the declaration of $j
There are several ways this can be accomplished:
1. Have a feedback loop between Parrot and Perl 6 that allows the
compiler to execute a chunk of bytecode, get the result as a PMC
and store it for future use. This will probably be needed
regardless of which option is chosen, but may not be ideal.
2. Have a pseudo Perl 6 interpreter in the compiler which can
execute a limited subset of Perl 6 that is allowed inside of
class and module definitions (Larry implied that they were not
limited in this way, but if they were, compilation could be
optimized a bit).
3. Attempt to build a one-shot, bytecode stream that outputs a
bytecode stream that represents the program. This would be the
fastest in the general case, and would make pre-bytecoded
libraries much easier to implement. However, it would also mean
that class and module definitions could not affect the grammar
of the language, and Larry has said that won't be the case :-(
To me, #2 looks most attractive, but requires some duplication of
effort.
How easy would it be to interact with Parrot in the way that #1
proposes?
--
Aaron Sherman <a...@ajs.com>
Senior Systems Engineer and Toolsmith
"It's the sound of a satellite saying, 'get me down!'" -Shriekback
Erm... no. Not even close, really. There's really nothing at all
special about this--it's a very standard user-defined type issue,
dead-common compiler stuff. You could, if you wanted, really
complicate it, but there's no reason to and unless someone really
messes up we're not going to. Just no need.
--
Dan
--------------------------------------"it's like this"-------------------
Dan Sugalski even samurai
d...@sidhe.org have teddy bears and even
teddy bears get drunk
>> More to the point, Perl 6's compiler will have to parse "class Joe",
>> create a new object of type Class, parse and execute the following
>> block/closure in class MetaClass, assign the result into the new Class
>> object named Joe and then continue parsing, needing access to the values
>> that were just created in order to further parse the declaration of $j
Dan> Erm... no. Not even close, really. There's really nothing at all
Dan> special about this--it's a very standard user-defined type issue,
Dan> dead-common compiler stuff. You could, if you wanted, really
Dan> complicate it, but there's no reason to and unless someone really
Dan> messes up we're not going to. Just no need.
Yes, there's no need to visit the construction jobsite to figure out
the length of a 2x4 crossbeam if you already have the blueprints in
front of you.
Unless you like to waste gas. :-)
--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<mer...@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
#1 is a requirement for the general case. Of course to the extent
that people stick with standard constructs they can be optimized.
But Perl 5's BEGIN blocks are just a start. In Perl 6 people can add
arbitrary productions into the grammar that execute arbitrary code.
They can define their own metaclasses. Traits are applied at compile
time by arbitrary bits of code. Macros are applied by executing bits
of code you just compiled. And so on.
Larry
Okay, how do you run a BEGIN block?
Larry
You call the anonymous sub that's been created for it. How *else*
would you run a BEGIN block?
But that's completely irrelevant to the original question, about
lexical variable classes. I presume you've got a point you're making?
Yes, my point is you didn't actually answer his question.
Larry
> >More to the point, Perl 6's compiler will have to parse "class Joe",
> >create a new object of type Class, parse and execute the following
> >block/closure in class MetaClass, assign the result into the new Class
> >object named Joe and then continue parsing, needing access to the values
> >that were just created in order to further parse the declaration of $j
>
> Erm... no. Not even close, really. There's really nothing at all
> special about this--it's a very standard user-defined type issue,
> dead-common compiler stuff. You could, if you wanted, really
> complicate it, but there's no reason to and unless someone really
> messes up we're not going to. Just no need.
That's not at all what A12 said. And, I quote:
One of the big advances in Perl 5 was that a program could be in
charge of its own compilation via use statements and BEGIN
blocks. A Perl program isn't a passive thing that a compiler has
its way with, willy-nilly. It's an active thing that negotiates
with the compiler for a set of semantics. In Perl 6 we're not
shying away from that, but taking it further, and at the same
time hiding it in a more declarative style. So you need to be
aware that, although many of the things we'll be talking about
here look like declarations, they trigger Perl code that runs
during compilation.
This is in direct contradiction to what I'm hearing from you, Dan.
What's the scoop?
The scoop is that
my Joe $foo;
emits the code that, at runtime, finds the class ID of whatever Joe's
in scope, instantiates a new object of that class, and sticks it into
the $foo lexical slot that's in scope at runtime. It's possible you
could redefine the grammar to have it do something different, in
which case I suppose the answer's "whatever you've convinced the
compiler to do" but in that case the question's essentially
meaningless, since you could, if you wanted, define it such that
invoking the perl 6 grammar reformatted your hard drives or something.
> >This is in direct contradiction to what I'm hearing from you, Dan.
> >What's the scoop?
>
> The scoop is that
>
> my Joe $foo;
>
> emits the code that, at runtime, finds the class ID of whatever Joe's
> in scope, instantiates a new object of that class, and sticks it into
> the $foo lexical slot that's in scope at runtime.
Right, ok, good. I gotcha.
But according to A12 as I understand it, the part BEFORE that, which
looked innocently like a definition:
class Joe { my $.a; method b {...} }
would actually get turned into a BEGIN block that executes the body of
the class definition as a closure in class MetaClass and stores the
result into a new object (named "Joe") of class Class.
Perl 6's compiler does not (by default, at least) know how to run code.
It just knows how to translate that text into bytecode (or IMCC or
something). So it will need SOMETHING to execute, possibly multiple
times with parsing going on before, after or during some of those
executions.
During is the hard one. That means you have to actually call back from
Parrot into the Perl 6 compiler. But, even the simple:
eval "eval 'eval 1' "
causes that problem. How does Ponie deal with that? Does it simply act
as an interpreter for the first pass and then do code-gen ala -MO ? If
so, that's a nice dodge, but putting a full Perl 6 interpreter into Perl
6's compiler seems to me to be a tad heavy-weight.
Thoughts? Am I missing a simple way to get around this?
There's where you're wrong. In the static languages world, they like to
make the target architicture independent of the compiler's architecture.
C++ does this (and runs into rather a problem doing so, because of the
treatment of templates).
But Perl 6 is tightly coupled with Parrot. Perl 6 will be a Parrot
program (even if it calls out to C a lot), and can therefore use the
compreg opcodes. That means that any code executing in Parrot can call
back out to the Perl 6 compiler, and obviously the Perl 6 compiler can
call out to parrot.
So:
eval "eval 'eval 1'";
#(a (b (c )) )
Goes through this process:
Perl 6 compiles (a)
Perl 6 tells Parrot to run (a)
Parrot calls out to Perl 6 to compile (b)
Perl 6 tells Parrot to run (b)
Parrot call out to Perl 6 to compile (c)
Perl 6 tells Parrot to run (c)
The result is 1
The result is 1
The result is 1
...
Luke
> But Perl 6 is tightly coupled with Parrot. Perl 6 will be a Parrot
> program (even if it calls out to C a lot), and can therefore use the
> compreg opcodes. That means that any code executing in Parrot can call
> back out to the Perl 6 compiler, and obviously the Perl 6 compiler can
> call out to parrot.
Clearly my question was garbled the first time, as this answer is
exactly what I was looking for. Thanks!
Uh, is that right? I don't think that "my" is a constructor, more a typing
declarator.
--
Hi, this is Ken. What's the root password?
Since any type potentially has assignment behaviour, it has to be a
constructor. For example, if you've got the Joe class set such that
assigning to it prints the contents to stderr, this:
my Joe $foo;
$foo = 12;
should print 12 to stderr. Can't do that if you've not put at least a
minimally constructed thing in the slot.
> Since any type potentially has assignment behaviour, it has to be a
> constructor. For example, if you've got the Joe class set such that
> assigning to it prints the contents to stderr, this:
>
> my Joe $foo;
> $foo = 12;
>
> should print 12 to stderr. Can't do that if you've not put at least a
> minimally constructed thing in the slot.
Yes, and to make that statement a bit more generic, I would suggest
that:
my X $y;
is, as far as I can tell:
my X $y = undef;
except that the explicit assignment might have a different signature
(being as you are providing a parameter to the constructor, even if it's
undef). There was a long thread a LONG time ago on what passing undef
meant for signature matching and how that interacted with default
arguments. I'm sorry, but I'm not able to recall the result at this
point. If I have time, I'll chase it down.
> At 11:29 PM +0100 4/22/04, Simon Cozens wrote:
>> d...@sidhe.org (Dan Sugalski) writes:
>>> my Joe $foo;
>>>
>>> emits the code that, at runtime, finds the class ID of whatever
>>> Joe's
>>> in scope, instantiates a new object of that class
>>
>> Uh, is that right? I don't think that "my" is a constructor, more a
>> typing
>> declarator.
>
> Since any type potentially has assignment behaviour, it has to be a
> constructor. For example, if you've got the Joe class set such that
> assigning to it prints the contents to stderr, this:
>
> my Joe $foo;
> $foo = 12;
>
> should print 12 to stderr. Can't do that if you've not put at least a
> minimally constructed thing in the slot.
Another way to look at that would be to say that this:
my Joe $foo;
$foo = $a;
is equivalent to either this pseudo-code:
my $foo;
assert_type($a, "Joe"); # raise if $a isn't of type Joe
$foo = $a;
or this:
my $foo;
$foo = new Joe($a);
depending on the semantics that Perl6 wants for this. My point there is
that you can get the semantics of "the variable is typed" without
having to construct anything as a result of the declaration. In terms
if your example, if Perl6 will let you define Joe such that your code
prints to stderr, then it might compile down to the equivalent of:
$foo = new Joe();
$foo.assign($a);
The question is really just which semantics Perl6 is intending ("my
Joe" is dynamic type checking v. autovivification). Parrot could
support any of them--it's just a compilation issue.
(And of course, in the case of "$foo = 12", some checking in each
scenario could be done at compile time.)
JEff
Yes. More typically, the type of the value needs to be checked
at assignment time, if we can't intuit its safety at compile time.
From an abstract point of view the constructor in question isn't so much
constructing a Joe object as a container for a Joe object--though, of
course, optimizations happen. So in the Parrot engine the containers
and containees are a bit more incestuous than it would appear from
the abstract language level. That's okay.
Larry
> Since any type potentially has assignment behaviour, it has to be a
> constructor. For example, if you've got the Joe class set such that
> assigning to it prints the contents to stderr, this:
>
> my Joe $foo;
> $foo = 12;
>
> should print 12 to stderr. Can't do that if you've not put at least a
> minimally constructed thing in the slot.
(hypothetical pre-breakfasty musings)
Such as a PerlUndef with the 'expected_type' property set to 'Joe'?
(and now for breaky)
-- c
Sure, that's doable, as are a number of other things. Depends on the
semantics of assignment to uninitialized types, though that's on a
per-type basis. This all gets somewhat messy when you mix in objects
and types, since what you'd do for storage depends on a number of
factors. (Like we want a low-level int if that was "my int $foo", a
generic restriction on what it can hold a reference to if Joe's just
a dull class, or impose assignment semantics on if it's a bit more
active)
So what does "$foo = 12" in that context actually mean in Perl6? I
_think_ there are three possibilities for what it could mean:
1) Store 12 in the $foo variable. Since 12 isn't a Joe, that's an error.
2) Attempt to convert 12 to a Joe, and store that. If (and only if)
that can't be done, it's an error.
3) Take any existing Joe in $foo, and call some setter method, passing
12 as a parameter to that method. (This one has implications for what
happens if $foo doesn't yet hold a Joe v. if it does.)
It sounded like you were starting to say (1), but then that wouldn't
ever cause 12 to be printed to stderr (since no assignment operator
would be called).
JEff
I was saying #3, though #3 and #1 do the same thing in most cases for
reference values. (Which, last I checked, perl 6 was doing for its
objects. But, being objects, I tend to be a bit behind there :)
Another interesting question is "in Perl 6, are variables typed, values typed,
or a little of both?"
It seems that Parrot has been working primarily on the assumption that it's
values that are typed, and punting variable typing to the IMCC code generation
layer.
--
You're not Dave. Who are you?
That's my worry--whether we have a problem. (No problem with typed
values, of course.) If variables are typed, _and_ that typing is
lexically scoped, then I think that we don't have a problem, and it can
be handled at the compilation level, by inserting the appropriate type
checks right before each assignment.
JEff
Sort of an "All's fair in love and war and assignment." :-)
I think all three of your behaviors are possible from your other
message. #2 comes about if there's a declared coercion operator
between the classes. But maybe that's just a check the compiler
wraps around the assignment. Dunno. I try to stay out of the SMOPs,
and occasionally succeed.
Larry
Well, at minimum, the typing of lexically scoped variables is lexically
scoped.
I'd say the typing of "our" variables should work the same. Maybe we
could throw in a check to make sure different lexical scopes don't
apply different types. (Or maybe that's just a feature...)
Larry
It also depends on whether the type is on the name or the container.
Which is your call. :-P (Though, given my druthers, I'd leave it on
the container)
Well, it depends on what you consider a variable and what you
consider a value, though we do have things rather mashed up in spots.
There are three components to all this:
*) The name
*) The container
*) The value
The name is the slot in whatever nametable you're looking in--the
global tables or lexical pads or whatever. As far as we're concerned
the name is untyped, and you can stick anything in there you want.
(Which'd correspond to perl6's rebinding)
The container holds a value, and it is typed. (The container, that
is) This is represented (generally) by a PMC. The container is
responsible for maintaining type integrity, and Doing The Right Thing
when handed a value.
The value is what's inside the PMC. It's often a low-level type
(string, int, or number) but in many cases, especially (though not
universally) the case of objects, it's another PMC. The value is also
typed.
The container determines what happens with get and set actions on the variable.
The value determines what happens with other (add, subtract,
multiply... anything overridable) actions on the variable.
In some (really many) cases the container and value are represented
by a single entity--that is, the container PMC provides the semantics
for the value in the PMC. This is what happens with the PerlScalar
(and its subtypes) PMC. These are, for all intents and purposes,
value types.
In other cases, the container and the value are represented by two
separate entities. The container's still a PMC, but a reference PMC,
and it delegates all value entries to the referred-to PMC. These, as
you'd expect, are reference types.
There are some interesting issues when mixing value and reference
types. Not interesting in the good sense, of course, but, then, when
is it ever?
Objects, FWIW, don't *have* to be reference types, and the current
ParrotObject scheme actually has them as value types, which needs
fixing--they ought to be reference types, so assignment rebinds the
container to a new value. OTOH, the semantics there for perl are a
bit mushy.
And yes, if anyone's wondering--this muddled split between value and
container are likely to bite us in annoying ways in the future,
and'll probably go down as Parrot's single biggest design flaw. (Even
if we decide to go with cuneiform as the base system for string
constants...(
We should certainly leave them on the container. The sin of rebinding
a name to a different type could be checked for specially, I expect.
Larry
Which begs the question of what happens when you do, something like:
my Dog $spot;
some_func(\$spot);
...
sub some_func($thing) {
$$thing = Cat.new();
}
It could be argued that our current PerlUndef is all very well, but it
confuses the roles of container and value. Maybe the declaration
above should translate to something like:
new $P0, 'PerlScalar';
$P0.set_type('Dog');
store_lex '$spot', -1, $P0
Yep, that's exactly the case I was thinking about. If the typing is
truly lexical, then that shouldn't be an error, but then "my Dog" only
guarantees that assignments in the same lexical scope can only put Dogs
in $spot, not that $spot will only ever hold a Dog. But that doesn't
seem very useful.
> It could be argued that our current PerlUndef is all very well, but it
> confuses the roles of container and value.
The way other languages typically handle this is via static typing.
That is, rather than having a "container object", they do their type
checking statically, so for cases like this either some_func() would
have to be prototyped as taking a Dog parameter, or you would get an
error trying to pass $spot into it.
It feels like we are treading in uncharted territory here, and it could
be a bit of cake-and-eat-it-ness. Let's look at what other languages
do:
1) C, C++, Java--all statically typed, with enforcement at compile time
(and dynamic, checked casts). They don't help much.
2) Smalltalk--completely dynamic; no typing of declarations. Also
doesn't help.
3) Objective-C--dynamic typing, but with optional static-style
declarations. Sounds promising. But it turns out that these only allow
compile-time warnings, but do nothing at runtime. (Type mismatches are
only warnings at compile time, and statically-typed variable can be
passed without warning into a method typed as taking a generic object
reference.) Hmm, still not much help.
I don't (right off) know of any other language which has something "in
between" variables and objects. That is, containers. They're feeling a
bit artificial.
But this is just off the top of my head.
> Maybe the declaration above should translate to something like:
>
> new $P0, 'PerlScalar';
> $P0.set_type('Dog');
> store_lex '$spot', -1, $P0
Yeah, I've been wondering how a Perl6 declaration is intended to
compile. It seems like that should be the most basic thing--the first
thing spelled out by parrot. But, it's kind of not clear.
JEff
Well... Perl 5.
Luke
Well, there's that. :)
But--how does that manifest itself? An SV would count as what I meant
by "object". What ends up being the container?
JEff
It's both, kinda. The SV is the container and the value, except when
it's an object, in which case there are two SVs, one of which is the
value and one the container.
PerlUndef's behaviour is one of the Parroty things that makes me rather
nervous. It blurs (obliterates) the line between container and
value rather spectacularly. ISTM that declaring 'my Joe $foo' should
create a PerlScalar with an expected type of Joe and a PerlUndef as its
contents. The PerlScalar's definedness test would simply be a test for
the definedness of its contents.