TSa wrote:
Daniel Ruoso wrote:
The problem is that you can't really know wether a value is immutable or
not, we presume a literal 1 to be immutable, but even if you
receive :(Int $i), it doesn't mean $i is immutable, because that
signature only checks if $i ~~ Int, which actually results in
$i.does(Int).

I fully agree that immutability is not a property of types in a
signature. But a signature should have a purity lock :(Int $i is pure)
that snapshots an object state and puts that into $i and also instructs
the underlying object system to check for purity violation in the scope
of the signature. The presence of this trait in methods called on $i and
parameters $i is given to can be checked by the compiler. A save mode
would be to allow only calls that have the trait. This is far from a
halting problem. Simple coverage analysis suffices.
<snip>

I think in practice, unless Perl 6 is significantly updated, this really comes down to trust.

We can do a lot of tests ahead of time, but in some respects an implementation shouldn't necessarily be forced to be immutable internally but rather would need to be trusted to externally do what it promises, which is provide a consistent unchanging snapshot at least during the course of a pure environment. I'm thinking specifically related to things like laziness or caching. If you have some Set doing objects and you want to, say, union them to produce another set, then fundamentally that is a pure operation on pure inputs. However internally said Set objects may perform some hashing of their elements to help determine uniqueness or equality and they may wait until the union is requested to calculate these; moreover, once calculated they probably want to cache that result for the next time someone does an operation that could benefit. Strictly speaking that cache is a mutation, at least unless you have some immutable value representing the whole cache and you derive another copy of the other version (mostly referring to the first plus delta for efficiency).

In some ways we basically have to trust the programmer to supply code that does what they say it will, and if it doesn't then the program misbehaves and the programmer has to deal with it. This won't effect other people using Perl or our library etc that don't use that specific programmer's code. It mainly starts to be an issue if someone is executing code from someone else without examining the code first.

This situation also seems similar to the idea of having private object attributes or what have you. If someone really wants to break the privacy they will, regardless of whether we have a closed unlocked door or a locked door.

So to summarize, I think in order to make it easier for us to say write pure libraries that get what they expect (determinism, isolation, atomicity, etc) regardless of what users do, Perl itself will possibly need more enhancements to enforce that, and where those end, it comes down to trust.

Like the limited warranty thing; it will perform correctly if you use it 
correctly.

We can do input checks or whatever in our library, but after a point we're just doing these checks to give more meaningful error messages to users, and after a point if there is a failure on something we didn't check, then the users have to figure it out due to their improper input.

And remember that by users I mean other programmer-written code in the same process as us, not external programs or human users. But in those cases we're not interacting by shared memory anyway, rather going by exchanges of undifferentiated bytes, so its not like they can pull tricks on us that normal proper input checking won't get.

Getting purity right down to the point where you almost don't have to trust your users to do the right thing in order for you to do the right thing is a fundamental issue, and you almost have to design the system on a foundation of purity of pseudo-purity in order to get that, eg taking lessons from Haskell.

As for working from where we are, well I like your snapshot idea, which is related to caching. If your declared parameter type is of an immutable type, then you snapshot your argument, a no-op if it actually is immutable, or otherwise what you actually snapshot is the result of invoking any operators on said argument, recursively on their results and so on ... its like memoization ... if the thing claims to be immutable then we can memoize any result from using it and just use that version afterwards rather than consulting the original. Where that works.

multi infix:<+> (int where { 2 } $i, int where { 2 } $j) {
 say "The Big Brother is Watching You!"
 return 5;
}

Does a printout already constitute a side-effect? If so, it is at
least not a dangerous one because it doesn't feed changed state
back into the stream of a computation. The only danger is that a
user wonders about the sequence of prints in the course of an optimized
flow. IOW, say could have the 'is pure' trait on its parameters and
on itself.

I will note that my definition of purity also includes being deterministic, isolated, and effectively atomic. I would consider a printout to be a side effect that strictly speaking wouldn't belong in a pure system. However, having a printout strictly for the purposes of debugging is okay.

-- Darren Duncan

Reply via email to