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