[email protected] wrote: > Thank you for the writeup; interesting. Just one point of motivation > that perhaps I missed from the original post: > > On Mon, Dec 7, 2009 at 11:17 PM, David-Sarah Hopwood > <[email protected]> wrote: >> To dodge this issue, let's provisionally call a function *instance* >> "copacetic" [*] if: >> - that instance has only captured copacetic values, and >> - it has no side effects and is deterministic whenever it is >> only called with copacetic argument values, and >> - it uses no side-effecting or nondeterministic primitives. > > I'm getting at something similar but distinct, call it "i-copacetic". > :) Specifically: > > - it has no side effects on its lexical environment regardless > of its argument values
You're right, that's a better definition. The implementation sketch I gave for 'copacetic' actually ensures that property already, even though the definition above doesn't. That's because the verification that captured variables are const is done statically, and the check that captured values are copacetic is done for each instantiation of a function marked as /*...@functional*/, before any call to that instance. Note that this implies that the lexical environment of each successfully created instance is observationally immutable, not just that it can't be directly changed by that instance. I.e. it also can't be changed indirectly via an operation on an argument (even if the checks for arguments being copacetic are dropped as discussed below), or by other code. > The motivation is this: An object's state is managed by some > surrounding system. However, it is allowed to expose "read()" services > to the outside world that do not participate in this state management. > Each "read()" service may side-effect the supplied arguments, but it > must not side-effect the lexical environment of the service (i.e., the > object itself). The implementation sketch I gave would preclude that use case because if the supplied argument values are not copacetic, the object's read() method would throw. This might have been too conservative -- if those argument checks were omitted then the property above (that the function instance can't change its lexical environment) would still hold. Similar cases occur for higher order functions. For example 'map' or 'fold' functions have no side effects except via their arguments. With the checks that arguments and result are copacetic, you would need both /*...@functional*/ and non-/*...@functional*/ variants of each such function, with the same implementation. If the argument *and* result checks were omitted (as David Wagner suggested), the same function could serve both cases. Another motivation, of course, is that not doing these checks is more efficient. It would be possible to make the argument and result checks optional depending on the function annotation: say, /*...@pure*/ includes them but /*...@functional*/ doesn't. (The environment checks would be the same, and both /*...@pure*/ and /*...@functional*/ would mark instances as copacetic.) In that case, /*...@pure*/ would imply unconditional determinism (referential transparency), whereas /*...@functional*/ would only imply determinism for calls for which all arguments are copacetic. What a fun discussion! Thanks for starting it. -- David-Sarah Hopwood ⚥ http://davidsarah.livejournal.com
signature.asc
Description: OpenPGP digital signature
