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. Larry