Re: Why pass by reference?
On Fri, 19 Jun 2009, Martin D Kealey wrote: > To that end I would propose that: > - parameters should be read-only AND invariant by default, and > - that invariance should be enforced passing a deep immutable clone >(*5) in place of any object that isn't already immutable. Sorry, typo: that last word should have been "invariant", meaning that it *won't* change, rather than "immutable", meaning that it *can't*. Compilers can rely on invariance to perform a range of very powerful optimizations; immutability is one way to guarantee invariance, but not the only way. -Martin
Re: Why pass by reference?
> Matthew Walton wrote: > > If a user of your API contrives to make it change while you're > > running, that's their own foot they've just shot, because they can > > look at the signature and know the semantics of the parameter > > passing being used and know that if they change the value externally > > before you return Bad Things Could Happen. > On Tue, 16 Jun 2009, TSa wrote: > I agree that the caller is responsible for the constness of the value > he gives to a function. With this we get the best performance. At the language level this is wrong. Programmers are BAD at this sort of thing, unless the compiler *always* has enough to throw a compile-time error, and even then it's dicey because we may defer compilation. It seems to me this is pushing something onto the author of the caller that they shouldn't have to deal with, especially when you consider that the parameter they're passing into the function may come from somewhere else, which hasn't been made -- and indeed CAN'T be made -- to promise not to meddle with the value (note *1). If the compiler can't spot it, how do you expect a fallible human being to do so? If a function requires an invariant parameter then the compiler should ensure that that guarantee is met, and not rely on the programmer to do something that is impossibly hard in the general case. A simple way would be to call $parameter := $parameter.INVARIANT() (*2) on the caller's behalf before calling the function. Conversely, when calling a function where the parameter is declared :rw, the compiler can call $parameter := $parameter.LVALUE() (*3) on the caller's behalf first if it needs to convert an immutable object to a mutable one. (Or throw up its hands and assert that it's not allowed.) If we really expect the optimizer to make Perl6 run well on a CPU with 1024 cores (*4), we have to make it easy to write programs that will allow the optimizer to do its job, and (at least a little bit) harder to write programs that defeat the optimizer. To that end I would propose that: - parameters should be read-only AND invariant by default, and - that invariance should be enforced passing a deep immutable clone (*5) in place of any object that isn't already immutable. -Martin Footnotes: *1: There are many possible reasons, but for example the caller didn't declare it :readonly in turn to its callers because it *did* plan to meddle with it -- but just not by calling this function with its :readonly parameter. *2: Yes I made up "INVARIANT". The trick is that the compiler only needs to insert the call if can't prove the invariance of $parameter, which it *can* prove when: - it arrived in a :readonly parameter; or - it's locally scoped, and hasn't "escaped". In addition the implementation of INVARIANT() could: - return $self for any "value" class; and - return the encapsulated immutable object for the case outlined in the following footnote. Otherwise the default implementation of INVARIANT() would be like deepclone(). (Declaring a "value class" would ideally be shorter than declaring a "container class", but I'm a bit stuck as to how to achieve that. Ideas are welcome...) *3: The LVALUE method produces the sort of proxy object that others have described, but with the reverse function: it acts as a scalar container that can only hold immutable objects, and proxies all method calls to it, but allows assignment to replace the contained object. Calling INVARIANT on such a container object simply returns the encapsulated immutable object. *4: As a generalization, the assumptions floating round that "the compiler will optimize things" just aren't facing reality: programmers are about the worst people when it comes to learning from the past mistakes of others, and future generations of Perl6 programmers will inevitably create evil container classes with no corresponding value classes, and thus most parallelizing optimizations will be defeated. *5: At the language level at least, copying is NOT the enemy of optimization. On the contrary, if you always copy and *never* mutate, that ensures that the compiler can always determine the provenance and visibility of any given datum, and thus has *more* opportunities to avoid *actually* copying anything. And it can parallelize to the full extent of available hardware because it can guarantee that updates won't overlap.
Re: Why pass by reference?
TSa Thomas.Sandlass-at-vts-systems.de |Perl 6| wrote: HaloO, Matthew Walton wrote: If a user of your API contrives to make it change while you're running, that's their own foot they've just shot, because they can look at the signature and know the semantics of the parameter passing being used and know that if they change the value externally before you return Bad Things Could Happen. I agree that the caller is responsible for the constness of the value he gives to a function. With this we get the best performance. I don't understand why John thinks that an intermediate proxy is needed. A very shallow wrapper that ensures the readonlyness suffices. Most of the time not even that when the constness is known statically. Regards TSa. "shallow wrapper" is what I'm talking about. That is indeed a proxy: if a full-blown run-time check is needed (when it gets passed beyond its ability to track at compile time) it forwards methods, intercepts others, and modifies accessors.
Re: Why pass by reference?
HaloO, Matthew Walton wrote: If a user of your API contrives to make it change while you're running, that's their own foot they've just shot, because they can look at the signature and know the semantics of the parameter passing being used and know that if they change the value externally before you return Bad Things Could Happen. I agree that the caller is responsible for the constness of the value he gives to a function. With this we get the best performance. I don't understand why John thinks that an intermediate proxy is needed. A very shallow wrapper that ensures the readonlyness suffices. Most of the time not even that when the constness is known statically. Regards TSa. -- "The unavoidable price of reliability is simplicity" -- C.A.R. Hoare "Simplicity does not precede complexity, but follows it." -- A.J. Perlis 1 + 2 + 3 + 4 + ... = -1/12 -- Srinivasa Ramanujan
Re: Why pass by reference?
> Complex or not in that sense, it complicates things in allowing the value to > be changed by another path. I think that is something we want to avoid > doing, not present as a feature. Much of my original post concerns the > actual meaning, not whether it is considered simple. > > Since then, I see that it is useful for plural containers. We don't want to > copy them! But for items, why do we not even _have_ pass by value? The > compiler must assume the worst and can't optimize as well. 'is copy' is pass-by-value... remember everything in Perl 6 is a reference, of sorts. Pass-by-value of the reference is covered by 'is ref'. A more useful variant of that being 'is rw', which gives you an extra assurance with its lvalue checking that the user's not giving you something that's going to explode when you try to modify it. Pass-by-value of the thing the reference points to is covered by 'is copy', which is the semantics people would generally expect when they hear 'pass-by-value'. Pass-by-reference-but-don't-accidentally-change-what-it-points-to is covered by the default case or 'is readonly'. This seems to me to be the ideal - we don't copy huge values around when the user doesn't need them, but we also don't have hugely dangerous mutable parameters by default (they should be extremely explicit for the user of an API). Most of the time, there won't be another path where the value could change. Under a threaded model allowing shared variables, sure it could be changed by another thread, but hopefully you're under lock there. If a user of your API contrives to make it change while you're running, that's their own foot they've just shot, because they can look at the signature and know the semantics of the parameter passing being used and know that if they change the value externally before you return Bad Things Could Happen. Matthew
Re: Why pass by reference?
> Complex or not in that sense, it complicates things in allowing the value to > be changed by another path. I think that is something we want to avoid > doing, not present as a feature. Much of my original post concerns the > actual meaning, not whether it is considered simple. > > Since then, I see that it is useful for plural containers. We don't want to > copy them! But for items, why do we not even _have_ pass by value? The > compiler must assume the worst and can't optimize as well. 'is copy' is pass-by-value... remember everything in Perl 6 is a reference, of sorts. Pass-by-value of the reference is covered by 'is ref'. A more useful variant of that being 'is rw', which gives you an extra assurance with its lvalue checking that the user's not giving you something that's going to explode when you try to modify it. Pass-by-value of the thing the reference points to is covered by 'is copy', which is the semantics people would generally expect when they hear 'pass-by-value'. Pass-by-reference-but-don't-accidentally-change-what-it-points-to is covered by the default case or 'is readonly'. This seems to me to be the ideal - we don't copy huge values around when the user doesn't need them, but we also don't have hugely dangerous mutable parameters by default (they should be extremely explicit for the user of an API). Most of the time, there won't be another path where the value could change. Under a threaded model allowing shared variables, sure it could be changed by another thread, but hopefully you're under lock there. If a user of your API contrives to make it change while you're running, that's their own foot they've just shot, because they can look at the signature and know the semantics of the parameter passing being used and know that if they change the value externally before you return Bad Things Could Happen. Matthew
Re: Why pass by reference?
Daniel Ruoso daniel-at-ruoso.com |Perl 6| wrote: Actually, it only looks complicated while you think only on the callee side. No, in general it requires introducing a read-only proxy in front of the container. This may be optimized away when it can be tracked at compile-time, but that's certainly not "simple" as compared to not having it nor the aliased item container at all. Because when you take the caller side, you'll note that it builds a capture to send to the call, and the capture is always a reference, so the signature just makes sure that references becomes read-only. To illustrate: my $a = 1; foo($a); In this case, the capture sent must contain a direct reference to the scalar held in '$a', so both signatures with "is ref" or signatures with "is copy" can work. So, if foo has the signature sub foo($a is ref) {...} it will be able to change the scalar outside &foo. If it is sub foo($a) {...} It will be a read-only access to that scalar sub foo($a is rw) {...} Works almost like "is ref", but encloses immutables into a container in order to always provide rw semantics. No, "is rw" does not like immutables. It will cause autovivification to take place, but will not accept something that is not an lvalue such as 1 or "Hello" literals. This was just doubled-checked with S06, S09, and discussion with Larry in #perl6. If Ra rakudo: sub foo($a is rw) { $a += 1; say $a }; foo(1); rakudo 77f9d7: OUTPUT«2» that directly contradicts S06, which states "Otherwise the signature fails to bind, and this candidate routine cannot be considered for servicing this particular call." Doing otherwise affects the semantics of MMD for allowing overloading based on whether the parameter is an lvalue or not. Somebody who works with rakudo could submit a bug, if it's not in there already? sub foo($a is copy) {...} Is the completely opposite to "is ref", copying the actual value to a new container. Agreed. So, it is not at all complicated, it's just oriented to the Capture, and the capture provides semantics to the call that are not present in any other language I'm aware of. Complex or not in that sense, it complicates things in allowing the value to be changed by another path. I think that is something we want to avoid doing, not present as a feature. Much of my original post concerns the actual meaning, not whether it is considered simple. Since then, I see that it is useful for plural containers. We don't want to copy them! But for items, why do we not even _have_ pass by value? The compiler must assume the worst and can't optimize as well. --John
Re: Why pass by reference?
Em Dom, 2009-06-14 às 15:53 -0500, John M. Dlugosz escreveu: > In Perl 6, the default parameter passing is to make a read-only alias > for the caller's lvalue. This means that the function may not change > the caller's variable, but must track changes to it made by other means. > What is the point? > It is a contrivance to illustrate how the variable can be changed by > other means, and requires a global variable, the same variable passed as > two different parameters, or the variable and a closure that affects the > variable be passed. Actually, it only looks complicated while you think only on the callee side. Because when you take the caller side, you'll note that it builds a capture to send to the call, and the capture is always a reference, so the signature just makes sure that references becomes read-only. To illustrate: my $a = 1; foo($a); In this case, the capture sent must contain a direct reference to the scalar held in '$a', so both signatures with "is ref" or signatures with "is copy" can work. So, if foo has the signature sub foo($a is ref) {...} it will be able to change the scalar outside &foo. If it is sub foo($a) {...} It will be a read-only access to that scalar sub foo($a is rw) {...} Works almost like "is ref", but encloses immutables into a container in order to always provide rw semantics. sub foo($a is copy) {...} Is the completely opposite to "is ref", copying the actual value to a new container. So, it is not at all complicated, it's just oriented to the Capture, and the capture provides semantics to the call that are not present in any other language I'm aware of. daniel