This is mostly just a gratuitous message so that Piers has something to talk about in the next summary ;-), but when's the next Apocalypse due out?
In these quiet times, it would be nice to hear from the language design team every now and then even if nothing but a "still working". (Better would be "We're working on X and have hashed out the details of Y but are having problems with Z", but I can see how that could cause all sorts of spurious chatter)
-Scott -- Jonathan Scott Duff d...@lighthouse.tamucc.edu
Jonathan Scott Duff wrote: > This is mostly just a gratuitous message so that Piers has something > to talk about in the next summary
I bet Leon has something to say about that.
> Better would be "We're working on X and have hashed out the details > of Y but are having problems with Z"
Something like: "We're working on Perl 6 and have hashed out the details of Perl 6 but are having problems with users who keep wanting regular updates" :-)
> This is mostly just a gratuitous message so that Piers has something > to talk about in the next summary ;-), but when's the next > Apocalypse due out?
Well, I don't know if Leon (Hi Piers!) has better information than I do, but the short answer is "Not for a while". Next Apocalypse is objects, and that'll take time. Damian may well get E7, formats, out sooner, but he's on vacation for the first time in too long, so he'd better not answer for a few weeks. :)
Dan Sugalski <d...@sidhe.org> writes: > Next Apocalypse is objects, and that'll take time.
Objects are *worth* more time than a lot of the other topics. Arguably, they're just as important as subroutines, in a modern language.
Speaking of objects... are we going to have a built-in object forest, like Inform has, where irrespective of class any given object can have up to one parent at any given time, which can change at runtime, and be able to declare objects as starting out their lives with a given parent object, move them at runtime from one parent to another (taking any of their own children that they might have along with them), fetch a list of the children or siblings of an object, and so forth?
> > Next Apocalypse is objects, and that'll take time.
> Objects are *worth* more time than a lot of the other topics. > Arguably, they're just as important as subroutines, in a modern > language.
> Speaking of objects... are we going to have a built-in object forest, > like Inform has, where irrespective of class any given object can have > up to one parent at any given time, which can change at runtime, and > be able to declare objects as starting out their lives with a given > parent object, move them at runtime from one parent to another (taking > any of their own children that they might have along with them), fetch > a list of the children or siblings of an object, and so forth?
Not.. exactly that.
There are a lot of useful object systems around, and it's not like Perl to choose just one of them. In Perl 5, it was possible to change your parent I<classes> at runtime, but that's because data wasn't a part of Perl 5's (minimalist) classes. In Perl 6, classes associate attributes with themselves, so I imagine that it's only possible to switch parent objects, not parent classes.
And I also presume that an object can have as many parents as it likes.
Also, the "standard library", however large or small that will be, will definitely be mutable at runtime. There'll be none of that Java "you can't subclass String, because we think you shouldn't" crap.
On Sat, 13 Sep 2003, Luke Palmer wrote: > Also, the "standard library", however large or small that will be, will > definitely be mutable at runtime. There'll be none of that Java "you > can't subclass String, because we think you shouldn't" crap.
Java's standard class library is a mishmash of things that represent containers (variables) and things that represent values (and even some broken things that try to be both), with no syntactic help to distinguish them. And its syntax reserves "const" but doesn't use it for anything.
As long as we have "is rw" and its friends, we can -- with suitable care -- make sure that a subclass of a value-representing class is also a value-representing class, so there's no semantic need to say "never any subclasses" but we can still do CSE and other neat stuff at compile time.
Of course having a "no subclasses" tag means the compiler can change a method call into a direct subroutine call, but I would hope that method calling will be fast enough that it won't need to.
Will we require methods in subclasses to use the same signatures as the methods they're overriding?
>> Also, the "standard library", however large or small that will be, >> will definitely be mutable at runtime. There'll be none of that Java >> "you can't subclass String, because we think you shouldn't" crap.
> Java's standard class library is a mishmash of things that represent > containers (variables) and things that represent values (and even some > broken things that try to be both), with no syntactic help to > distinguish them. And its syntax reserves "const" but doesn't use it > for anything.
> As long as we have "is rw" and its friends, we can -- with suitable > care -- make sure that a subclass of a value-representing class is also > a value-representing class, so there's no semantic need to say "never > any subclasses" but we can still do CSE and other neat stuff at compile > time.
> Of course having a "no subclasses" tag means the compiler can change a > method call into a direct subroutine call, but I would hope that method > calling will be fast enough that it won't need to.
A strategy to actually keep that optimization, and apply it to much more code, would be that the JIT compiler could optimize for the case that there are no known subclasses, and pessimize that only if a subclass were later loaded.
I think this is one of the features Leo's been on about with respect to notifications. The class loader could fire a notification that a new subclass had been loaded. The subroutine which needs to pessimize should its assumption be violated would observe that notification, so that it could discard its compiled form and be re-JITted when next invoked. (This gets harder when the routine is running, though. Prior art: A good 2 years of infuriating instability in the 1.4.x JDK after HotSpot was introduced.)
Even better, the JIT could optimize for the case that there are no known overrides of a method, which would allow this optimization to apply to much, much more code. Or it could mix the two for reduced overhead: Observe classWasSubclassed if there are no subclasses at compile time. If there are subclasses at compile time, but no overrides of the method which was invoked, observe methodWasOverridden instead.
These strategies get the best of both dynamism and performance whenever possible, not just when the programmer felt like hamstringing himself in advance by declaring a class or method to be final.
Luke Palmer <fibon...@babylonia.flatirons.org> writes: > Also, the "standard library", however large or small that will be, will > definitely be mutable at runtime. There'll be none of that Java "you > can't subclass String, because we think you shouldn't" crap.
Great. But will it also be possible to add methods (or modify them) to an existing class at runtime? You only have to look at a Smalltalk image to see packages adding helper methods to Object and the like (better to add a do nothing method to Object that find yourself doing C<$thing.do_that if $thing.can('do_that')> all the time...)
Piers Cawley writes: > Luke Palmer <fibon...@babylonia.flatirons.org> writes: > > Also, the "standard library", however large or small that will be, will > > definitely be mutable at runtime. There'll be none of that Java "you > > can't subclass String, because we think you shouldn't" crap.
> Great. But will it also be possible to add methods (or modify them) > to an existing class at runtime?
Parrot supports it, so I don't see why Perl wouldn't.
> You only have to look at a Smalltalk image to see packages adding > helper methods to Object and the like (better to add a do nothing > method to Object that find yourself doing C<$thing.do_that if > $thing.can('do_that')> all the time...)
Agreed completely. Plus, there are some cool things you can do by mutating methods of Object, like implementing auto-rollback variables. (A6 C<let foo()> behavior).
Luke Palmer <fibon...@babylonia.flatirons.org> writes: > Piers Cawley writes: >> Luke Palmer <fibon...@babylonia.flatirons.org> writes: >> > Also, the "standard library", however large or small that will be, will >> > definitely be mutable at runtime. There'll be none of that Java "you >> > can't subclass String, because we think you shouldn't" crap.
>> Great. But will it also be possible to add methods (or modify them) >> to an existing class at runtime?
> Parrot supports it, so I don't see why Perl wouldn't.
>> You only have to look at a Smalltalk image to see packages adding >> helper methods to Object and the like (better to add a do nothing >> method to Object that find yourself doing C<$thing.do_that if >> $thing.can('do_that')> all the time...)
> Agreed completely. Plus, there are some cool things you can do by > mutating methods of Object, like implementing auto-rollback variables. > (A6 C<let foo()> behavior).
On Sun, 14 Sep 2003, Gordon Henriksen wrote: > On Saturday, September 13, 2003, at 11:33 , mar...@kurahaupo.gen.nz > wrote:
> > On Sat, 13 Sep 2003, Luke Palmer wrote:
> > Of course having a "no subclasses" tag means the compiler can change a > > method call into a direct subroutine call, but I would hope that method > > calling will be fast enough that it won't need to.
> A strategy to actually keep that optimization, and apply it to much more > code, would be that the JIT compiler could optimize for the case that > there are no known subclasses, and pessimize that only if a subclass > were later loaded.
> I think this is one of the features Leo's been on about with respect to > notifications.
That's one of the reasons notifications were designed in, yes. There's a growing body of interesting work on what's essentially disposable or partially-useful optimizations. Given the dynamic nature of most of the languages we care about for parrot, throwaway optimizations make a lot of sense--we can build optimized versions of functions for the current structure, and redo them if the structure changes.
This isn't entirely an easy task, however, since you can't throw away or redo a function/method/sub/whatever that you're already in somewhere in the call-chain, which means any optimizations will have to be either checked at runtime or undoable when code is in the middle of them. (Which is a decidedly non-trivial thing, and impossible in general, though possibly not in specific cases)
I don't see any reason to not allow marking a class as final (though that's not hugely useful), closed (which is far more useful), or declared objects as exact types rather than subtypable. (Which is, in conjunction with closing a class, hugely useful)
Which is to say that marking a class as unenhanceable and Foo variables as holding *only* objects of type Foo and not child classes, gets us a lot more than marking a class as final. (Which does the same thing, but globally, and not necessarily usefully)
> > Next Apocalypse is objects, and that'll take time.
> Objects are *worth* more time than a lot of the other topics. > Arguably, they're just as important as subroutines, in a modern > language.
Oh, I dunno -- it's not like there's all that much to objects, but I might be a touch biased here. (I'd say they're worth more time because people get so worked up over them, not because they're particularly complex, complicated, or difficult)
> Speaking of objects... are we going to have a built-in object forest, > like Inform has, where irrespective of class any given object can have > up to one parent at any given time,
Multiple parent classes, yes. Parent objects, no. (Unless you consider composition of objects from multiple parent classes with each class having instance variables in the objects as multiple parent objects. In which case the answer's yes)
> which can change at runtime,
Well, the inheritance hierarchy for a class can change at runtime, though we'd really rather you didn't do that, so I suppose you could do it for individual objects--they'd just get a transparent singleton class that you'd mess around with from there. I think I may be missing your point.
> and > be able to declare objects as starting out their lives with a given > parent object, move them at runtime from one parent to another (taking > any of their own children that they might have along with them), fetch > a list of the children or siblings of an object, and so forth?
Erm.... I don't think so. I get the feeling that Inform had a different view of OO than we do.
On Mon, 15 Sep 2003, Piers Cawley wrote: > Luke Palmer <fibon...@babylonia.flatirons.org> writes: > > Also, the "standard library", however large or small that will be, will > > definitely be mutable at runtime. There'll be none of that Java "you > > can't subclass String, because we think you shouldn't" crap.
> Great. But will it also be possible to add methods (or modify them) > to an existing class at runtime?
[Recipients trimmed back to just the list, because it had gotten very silly. When replying to someone who's on the list, there's no need to copy them personally, too; they just end up with duplicates. :)]
On 2003-09-15 at 09:21:18, Piers Cawley wrote:
> Great. But will it also be possible to add methods (or modify them) > to an existing class at runtime? You only have to look at a Smalltalk > image to see packages adding helper methods to Object and the like > (better to add a do nothing method to Object that find yourself doing > C<$thing.do_that if $thing.can('do_that')> all the time...)
No need to go as far as Smalltalk; just look at Ruby (which probably took the idea from Smalltalk, but that's beside the point. :)). There are all sorts of libraries that do their thing by adding methods to other classes. Not just to Object, but to specific built-in classes such as Time, Date, Numeric, Fixnum, String, etc.
I'm not saying that this is necessarily the cleanest way to implement an add-on; it's easy to argue that this sort of thing is best accomplished by a static class or module method that takes an instance of the class in question as a parameter. But it's a nice thing to have in the toolkit.
-- Mark REED | CNN Internet Technology 1 CNN Center Rm SW0831G | mark.r...@cnn.com Atlanta, GA 30348 USA | +1 404 827 4754
pdcaw...@bofh.org.uk (Piers Cawley) writes: > Great. But will it also be possible to add methods (or modify them) > to an existing class at runtime? You only have to look at a Smalltalk > image to see packages adding helper methods to Object and the like
People get upset when CPAN authors add stuff to UNIVERSAL:: :)
-- "Anyone attempting to generate random numbers by deterministic means is, of course, living in a state of sin." -- John Von Neumann
> pdcaw...@bofh.org.uk (Piers Cawley) writes: > > Great. But will it also be possible to add methods (or modify them) > > to an existing class at runtime? You only have to look at a Smalltalk > > image to see packages adding helper methods to Object and the like
> People get upset when CPAN authors add stuff to UNIVERSAL:: :)
> > On Saturday, September 13, 2003, at 11:33 , mar...@kurahaupo.gen.nz
> > wrote:
> > > On Sat, 13 Sep 2003, Luke Palmer wrote:
> > > Of course having a "no subclasses" tag means the compiler can > change a > > > method call into a direct subroutine call, but I would hope that > method > > > calling will be fast enough that it won't need to.
> > A strategy to actually keep that optimization, and apply it to much > more > > code, would be that the JIT compiler could optimize for the case > that > > there are no known subclasses, and pessimize that only if a > subclass > > were later loaded.
> > I think this is one of the features Leo's been on about with > respect to > > notifications.
> That's one of the reasons notifications were designed in, yes. > There's a growing body of interesting work on what's essentially > disposable or partially-useful optimizations. Given the dynamic > nature of most of the languages we care about for parrot, > throwaway optimizations make a lot of > sense--we can build optimized versions of functions for the current > structure, and redo them if the structure changes.
> This isn't entirely an easy task, however, since you can't throw away > or redo a function/method/sub/whatever that you're already in > somewhere in the call-chain, which means any optimizations will > have to be either checked at runtime or undoable when code is in > the middle of them.
Why is this?
Given that threads are present, and given the continuation based nature of the interpreter, I assume that code blocks can be closured. So why not allocate JITed methods on the heap and manage them as first class closures, so that the stackref will hold them until the stack exits?
Simon Cozens <si...@simon-cozens.org> writes: > pdcaw...@bofh.org.uk (Piers Cawley) writes: >> Great. But will it also be possible to add methods (or modify them) >> to an existing class at runtime? You only have to look at a Smalltalk >> image to see packages adding helper methods to Object and the like
> People get upset when CPAN authors add stuff to UNIVERSAL:: :)
Austin Hastings <austin_hasti...@yahoo.com> writes: >> There's a growing body of interesting work on what's essentially >> disposable or partially-useful optimizations. Given the dynamic >> nature of most of the languages we care about for parrot, throwaway >> optimizations make a lot of sense--we can build optimized versions >> of functions for the current structure, and redo them if the >> structure changes.
>> This isn't entirely an easy task, however, since you can't throw >> away or redo a function/method/sub/whatever that you're already in >> somewhere in the call-chain, which means any optimizations will >> have to be either checked at runtime or undoable when code is in >> the middle of them.
> Why is this?
> Given that threads are present, and given the continuation based > nature of the interpreter, I assume that code blocks can be > closured. So why not allocate JITed methods on the heap and manage > them as first class closures, so that the stackref will hold them > until the stack exits?
On Mon, 15 Sep 2003, Austin Hastings wrote: > --- Dan Sugalski <d...@sidhe.org> wrote: > > This isn't entirely an easy task, however, since you can't throw away > > or redo a function/method/sub/whatever that you're already in > > somewhere in the call-chain, which means any optimizations will > > have to be either checked at runtime or undoable when code is in > > the middle of them.
> Why is this?
Because there are some assertions that can lead the optimizer to make some fundamental assumptions, and if those assumptions get violated or redefined while you're in the middle of executing a function that makes use of those assumptions, well...
Changing a function from pure to impure, adding an overloaded operator, or changing the core structure of a class can all result in code that needs regeneration. That's no big deal for code you haven't executed yet, but if you have:
a = 1; b = 12; foo(); c = a + b;
and a and b are both passive classes, that can get transformed to
a = 1; b = 12; foo(); c = 13;
but if foo changes the rules of the game (adding an overloaded + to a or b's class) then the code in that sub could be incorrect.
You can, of course, stop even potential optimization once the first "I can change the rules" operation is found, but since even assignment can change the rules that's where we are right now. We'd like to get better by optimizing based on what we can see at compile time, but that's a very, very difficult thing to do.
Austin Hastings wrote: > Dan Sugalski <d...@sidhe.org> wrote:
> > There's a growing body of interesting work on what's essentially > > disposable or partially-useful optimizations. Given the dynamic > > nature of most of the languages we care about for parrot, > > throwaway optimizations make a lot of sense--we can build optimized > > versions of functions for the current structure, and redo them if > > the structure changes.
> > This isn't entirely an easy task, however, since you can't throw > > away or redo a function/method/sub/whatever that you're already in > > somewhere in the call-chain, which means any optimizations will > > have to be either checked at runtime or undoable when code is in > > the middle of them.
> Why is this?
> Given that threads are present, and given the continuation based > nature of the interpreter, I assume that code blocks can be closured. > So why not allocate JITed methods on the heap and manage them as first > class closures, so that the stackref will hold them until the stack > exits?
Austin,
That's a fine and dandy way to do some things, like progressive optimization ala HotSpot. (e.g., "Oh! I've been called 10,000 times. Maybe you should bother to run a peephole analyzer over me?") But when an assumption made by the already-executing routine is actually violated, it causes incorrect behavior. Here's an example:
my $plugin is My::PluginBase; $plugin = load_plugin($ARGV[0]); $plugin.SayHi();
Now, while it would obviously seem a bad idea to you, it would be reasonable for perl to initially optimize the method call $plugin.say_hi() to the function call My::PluginBase::say_hi($plugin). But when load_plugin loads a subclass of My::PluginBase from the file specified in $ARGV[0], then that assumption is violated. Now, the optimization has to be backed out, or the program will never call the subclass's say_hi. Letting the GC clean up the old version of main when the notification is received isn't enough--the existing stack frame must actually be rewritten to use the newly-compiled version.
--
Gordon Henriksen IT Manager ICLUBcentral Inc. gor...@iclub.com
On Mon, Sep 15, 2003 at 11:19:22AM -0400, Dan Sugalski wrote: > Changing a function from pure to impure, adding an overloaded operator, or > changing the core structure of a class can all result in code that needs > regeneration. That's no big deal for code you haven't executed yet, but if > you have:
> a = 1; > b = 12; > foo(); > c = a + b; > but if foo changes the rules of the game (adding an overloaded + to a or > b's class) then the code in that sub could be incorrect.
> You can, of course, stop even potential optimization once the first "I can > change the rules" operation is found, but since even assignment can change > the rules that's where we are right now. We'd like to get better by > optimizing based on what we can see at compile time, but that's a very, > very difficult thing to do.
Sorry if this is a crack fuelled idea, and sorry that I don't have a patch handy to implement it, but might the following work:
0: retain the original bytecode 1: JIT the above subroutine as if a and b remain integers However, at all the "change the world" points (presumably they are de facto sequence points, and will we need to take the concept from C?) put an op in the JIT stream "check if world changed" 2: If the world has changed, jump out of the JIT code back into the bytecode interpreter at that point
I fear that this would mean that the JIT wouldn't just have to insert lots of "has world changed" ops, but also an awful lot of fixup code that nearly never gets executed; code to stuff values back from processor registers into parrot registers.
Then again, as this code is rarely executed and isn't speed critical (after the world has changed you're likely to be in the the switch core, which isn't slow) maybe the fixup would actually be better as densely compressed special bytecode instructions on which processor registers to save where in the parrot interpreter struct.
Effectively the JIT code would have an escape hatch instruction for every debuggable position in the original parrot source. This seems expensive on space, but is the only way I can think of to implement JITting of (say) arithmetic on loops where there are calls midway which could tie/overload/ whatever the very variables used in the loops.
Nicholas Clark writes: > On Mon, Sep 15, 2003 at 11:19:22AM -0400, Dan Sugalski wrote:
> > Changing a function from pure to impure, adding an overloaded operator, or > > changing the core structure of a class can all result in code that needs > > regeneration. That's no big deal for code you haven't executed yet, but if > > you have:
> > a = 1; > > b = 12; > > foo(); > > c = a + b;
> > but if foo changes the rules of the game (adding an overloaded + to a or > > b's class) then the code in that sub could be incorrect.
> > You can, of course, stop even potential optimization once the first "I can > > change the rules" operation is found, but since even assignment can change > > the rules that's where we are right now. We'd like to get better by > > optimizing based on what we can see at compile time, but that's a very, > > very difficult thing to do.
> Sorry if this is a crack fuelled idea, and sorry that I don't have a patch > handy to implement it, but might the following work:
> 0: retain the original bytecode > 1: JIT the above subroutine as if a and b remain integers > However, at all the "change the world" points > (presumably they are de facto sequence points, and will we need to > take the concept from C?) > put an op in the JIT stream "check if world changed" > 2: If the world has changed, jump out of the JIT code back into the > bytecode interpreter at that point
No, I think Parrot will still only JIT I&N registers. Optimization includes way more than just JIT.
I was thinking, the compiler could emit two functions: one that works on regular PMC's, and one that works with I registers. The latter would then be JITted as usual, and JIT doesn't need to do anything special.
The focus here, I think, is the following problem class:
sub twenty_five() { 25 } # Optimized to inline sub foo() { print twenty_five; # Inlined &twenty_five := { 36 }; print twenty_five; # Uh oh, inlined from before }
The problem is we need to somehow un-optimize while we're running. That is most likely a very very hard thing to do, so another solution is probably needed.
>The problem is we need to somehow un-optimize while we're running. That >is most likely a very very hard thing to do, so another solution is >probably needed.
It is, indeed, a very hard problem. It's solvable if you disallow several classes of optimization (basically ones that involve code motion) that make things less than optimal. You can also scatter a lot of tests for invalidations and have the notification system set the flags, though there are still code motion problems there. (Loops are particularly troublesome) -- Dan
--------------------------------------"it's like this"------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk
>On Mon, Sep 15, 2003 at 03:30:06PM -0600, Luke Palmer wrote: >> The focus here, I think, is the following problem class:
>> sub twenty_five() { 25 } # Optimized to inline >> sub foo() { >> print twenty_five; # Inlined >> &twenty_five := { 36 }; >> print twenty_five; # Uh oh, inlined from before >> }
>> The problem is we need to somehow un-optimize while we're running. That >> is most likely a very very hard thing to do, so another solution is >> probably needed.
>A naive approach would be to cache the names and positions of things >that are optimized such that when one of the cached things are >modified, the optimization could be replaced with either another >optimization (as in the case above) or an instruction to execute some >other code (when we can't optimize the change).
That doesn't work in the face of code motion, reordering, or simplification, unfortunately. :( -- Dan
--------------------------------------"it's like this"------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk