Greetings. In implementing :=, I have discovered two different set of semantics in explantations. I will refer them as "linking" and "thunking".
The "linking" semantic is akin to hard links in filesystems. It takes the storage location in the RHS and binds its to the name in the LHS:
$x := $x; # no-op ($x, $y) := ($y, $x); # swap
The "thunking" semantic is akin to symbolic links in filesystems. It takes the expression in RHS and wraps it in an implicit closure, then give that closure a name to be triggered later. A12 has an example:
$endpos := $string.chars; # thunk, changes as $string changes
Now, those two semantics directly clash when the RHS can be interpreted both ways. One good example would be array dereference:
my ($x, @a); $x := @a[-1]; @a = (1..100); say $x;
Under the linking semantic, there is no location in RHS to bind yet. One possible interpretation is just autovivify it -- but [-1] is not autovivifiable, so it should throw out an fatal exception under the linking semantic right there. Under the thunking semantic, of course, it will work as expected.
Assuming the thunking semantics however, I am not too sure about how this can possibly work:
($x, $y) := ($y, $x); # swap?
One interpretation is that the RHS pad bindings are snapshotted, so future calls to $x always evaluates the bound "$y" at the RHS context, and hence give correct results. This would mean:
my ($x, @a); $x := @a[0]; @a := ($x, $x, $x); $x := 1; say @a; # (undef, undef, undef)
Okay, that looks good to me. Should I go ahead and implement the thunking semantics?
> Now, those two semantics directly clash when the RHS can be > interpreted both ways.
Not if methods for attributes like .chars promise to always return the same variable, which would make even more sense if they were lvalue methods. They can be put to use, like
.chars = 5; # truncate or pad
or they can be made read-only lvalues, as is a numeric literal:
$foo := 5; $foo++; # error.
> Under the linking semantic, there is no location in RHS to bind yet. > One possible interpretation is just autovivify it -- but [-1] is not > autovivifiable, so it should throw out an fatal exception under the > linking semantic right there. Under the thunking semantic, of course, > it will work as expected.
But when do you do when the index is non-literal?
$foo := @bar[$bar];
Does this work like creating an lvalue closure { @bar[$bar] } and calling it, does it bind to the @bar with $bar as it was at the moment of binding?
Is it true that every thunking thing is essentially a compile time bind, and linking(aliasing) is a runtime thing? If so, can the semantics each have their own operator? :=, ::=. I don't know if this makes any sense, but that has something to do with me never understanding the purpose of ::=.
hm, I'd expect @a to be (1, 1, 1) (WE = when evaluated): my ($x, @a); # $x is undef WE, @a is () WE $x := @a[0]; # $x is undef WE, @a is () WE @a := ($x, $x, $x); # $x is undef WE, @a is (undef, undef, undef) WE # But those "undef"s are bound to @a[0] -- # @a := (@a[0], @a[0], @a[0]); $x := 1; # $x is 1 WE, @a[0] is 1 WE say @a; # (1, 1, 1)
And: my ($x, @a); # $x is undef WE, @a is () WE $x := @a[0]; # $x is undef WE, @a is () WE @a = ($x, $x, $x); # $x is undef WE, @a is (undef, undef, undef) WE # Those "undef"s are real undefs $x := 1; # $x is 1 WE, @a[0] is 1 WE say @a; # (1, undef, undef)
Opinions?
--Ingo
-- Linux, the choice of a GNU | To understand recursion, you must first generation on a dual AMD | understand recursion. Athlon! |
On Sat, Apr 23, 2005 at 06:51:04PM +0800, Autrijus Tang wrote:
: Greetings. In implementing :=, I have discovered two different : set of semantics in explantations. I will refer them as "linking" and : "thunking".
Congratulations--you've rediscovered "call by ref" and "call by name", but computer scientists tend to associate those concepts with calls for some reason. :-)
In fact, the name "thunk" was invented for Algol, because it did call-by-name, and was widely reviled for its inability to swap two parameters. Nevertheless, computer scientists liked the "definitionalness" of call-by-name, and have used it to write pseudocode for years. It's hard to implement efficiently, but it does let you defer some decisions about lvalueness.
: The "linking" semantic is akin to hard links in filesystems. : It takes the storage location in the RHS and binds its to the : name in the LHS: : : $x := $x; # no-op : ($x, $y) := ($y, $x); # swap : : The "thunking" semantic is akin to symbolic links in filesystems. : It takes the expression in RHS and wraps it in an implicit closure, : then give that closure a name to be triggered later. : A12 has an example: : : $endpos := $string.chars; # thunk, changes as $string changes
As Juerd pointed out, a ref to an lvalue works just as well to track changes. The := operator is intended to use hard linking semantics.
: Now, those two semantics directly clash when the RHS can be : interpreted both ways. One good example would be array dereference: : : my ($x, @a); : $x := @a[-1]; : @a = (1..100); : say $x; : : Under the linking semantic, there is no location in RHS to bind yet. : One possible interpretation is just autovivify it -- but [-1] is not : autovivifiable, so it should throw out an fatal exception under the : linking semantic right there. Under the thunking semantic, of course, : it will work as expected.
I would prefer the exception.
: Assuming the thunking semantics however, I am not too sure about how : this can possibly work: : : ($x, $y) := ($y, $x); # swap? : : One interpretation is that the RHS pad bindings are snapshotted, : so future calls to $x always evaluates the bound "$y" at the RHS : context, and hence give correct results. This would mean: : : my ($x, @a); : $x := @a[0]; : @a := ($x, $x, $x); : $x := 1; : say @a; # (undef, undef, undef) : : Okay, that looks good to me. Should I go ahead and implement : the thunking semantics?
No, I think just treat the RHS as a context that says: "Give me an lvalue if you can, otherwise give me an rvalue". One of the motivations for making {...} always return a closure is so that it would be really easy to specify a thunk when you really want one. But the flip side of that is we want to discourage implicit thunking in favor of explicit thunking. One benefit of that is that we give the optimizer more information about the intent of the programmer. It will be better for efficiency if we don't have to clone a bunch of closures unnecessarily.
We could make some kind of ruling that binding closures to a container gives that closure the ability to proxy for the container if you use the container as something other than a closure. But there are probably inconsistencies in that approach, unless we only allow such binding to containers that could not be used directly as a a closure reference. Have to think about that some more.
On Sat, Apr 23, 2005 at 10:21:56AM -0700, Larry Wall wrote: > : Now, those two semantics directly clash when the RHS can be > : interpreted both ways. One good example would be array dereference: > : > : my ($x, @a); > : $x := @a[-1]; > : @a = (1..100); > : say $x; > : > : Under the linking semantic, there is no location in RHS to bind yet. > : One possible interpretation is just autovivify it -- but [-1] is not > : autovivifiable, so it should throw out an fatal exception under the > : linking semantic right there. Under the thunking semantic, of course, > : it will work as expected.
> I would prefer the exception.
Alright. I wish I had seen this mail earlier -- because I just implemented the call-by-name semantics with snapshotting of RHS pad.
Oh well. At least the same code can be salvaged to make iThreads and serializable continuations work.
So, hm. What does this do?
my ($x, @a); $x := @a[0]; # vivified or not? @a = (1..100); say $x;
On Sat, Apr 23, 2005 at 09:50:26PM +0200, Juerd wrote: > Autrijus Tang skribis 2005-04-24 3:47 (+0800): > > $x := @a[0]; # vivified or not?
> Vivified, because you're taking a reference (not at language level) and > you can't have a reference (at internal level) pointing to something > that doesn't exist. At language level, you can, but only symbolically.
Okay. Implemented as r2260, with full call-by-value semantics as defined by Larry's previous post. `bindings.t` passes under either semantics, as expected.
Please sanity-check the following:
pugs> my ($x, @a); $x := @a[-1]; $x = 3; @a *** Error: Modification of non-creatable array value attempted
Vivified, because you're taking a reference (not at language level) and you can't have a reference (at internal level) pointing to something that doesn't exist. At language level, you can, but only symbolically.
On Sun, Apr 24, 2005 at 03:37:23AM +0000, Nigel Sandever wrote:
: On Sun, 24 Apr 2005 03:47:42 +0800, autri...@autrijus.org (Autrijus Tang) wrote: : > : > Oh well. At least the same code can be salvaged to make iThreads : : Please. No iThreads behaviour in Perl 6. : : Nobody uses them and whilst stable, the implementation is broken in so many way. : : But worse, the underlying semantics are completely and utterly wrong.
Are you confusing iThreads with pThreads? Or are you recommending we go back to the pThreads fiasco?
From what I've read, the trend in most modern implementations of concurrency is away from shared state by default, essentially because shared memory simply doesn't scale up well enough in hardware, and coordinating shared state is not terribly efficient without shared memory. If you are claiming that modern computer scientists are completely and utterly wrong in moving that direction, well, that's your privilege. But you should be prepared for a little pushback on that subject, especially as you are merely badmouthing something without goodmouthing something else in it's place.