Re: Encapsulating the contents of container types

2011-09-09 Thread Carl Mäsak
Thanks to everyone for your replies. I've been wanting to write a
summary + further thoughts for a while, but have been a bit
time-constrained.

The central tension in this issue is nicely captured by what Damian
wrote contrasted with what Stefan wrote:

Damian ():
 Doing nothing should result in the safe behaviour (i.e. full encapsulation).
 You ought to have to explicitly request anything else.

 One of the *big* selling points of Perl 6 is the prospect of OO done right.
 Leaky encapsulation ain't that.

Stefan (]):
] I am [fine with leaky encapsulation], personally.  It's not a problem unique
] to Perl 6 and I have yet to see a solution (in any language) that fails
] to be worse than the original
] problem.

I find both sides very reasonable, each in its own way. I'd *like* to
see a solution materialize along the lines of what Damian recommends,
but it's not going to happen by itself and without a concrete proposal
for how it should work. If we just leave things be, what we'll end up
with will be the status quo ante bellum, along the lines of what
Stefan recommends.

I'd be fine with finding some part-way solution that would make life
easier and less monotonous for people who care about defensive
copying. I'd even be fine with defensive copying semantics being a
(non-core) module, but it would feel like defeat in some ways.

However, having read the whole thread, various pages on the web (see
references), and IRC discussions, I see a vaguely hopeful picture
emerging. Not sure where it leads yet.

Non-rw attributes, non-rw parameters, and non-rw return values all
share the same mechanism. Fine. Makes sense. The Rakudo devs have
introduced the concept of decontainerization for scalar values that
need to be stripped of their surrounding scalar container when they
are passed through any of these barriers. This makes the values
effectively immutable, since there's no container to put a new value
in. The concept doesn't seem to apply cleanly to container types, but
the parallel is perhaps noteworthy.

Non-rw-ness in this case should be all the way, not one level down.
If it's good, non-leaky encapsulation we care about, it *has* to be
all the way. One problem here is not hard-coding any semantics to the
usual Array and Hash types -- it has to work even for container types
we haven't dreamed up yet. It has to work for objects. Likely there'll
have to be some contract that authors of new container types will have
to adhere to.

Patrick Michaud said something (I think AFK) that seems essential to
me: the non-rw-ness of these objects isn't a trait of *the object
itself*, it's a trait of *the context in which the object is used*.
(Note the similarity to what Ruud suggested.) Some actual code shows
what this means:

 my %hash = foo = { bar = 42 }

 # works, shouldn't:
 sub parameter-barrier(%h) { %hfoobar = OH NOES; say %h }
 parameter-barrier(%hash)
(foo = {bar = OH NOES}).hash

 # works, shouldn't:
 sub return-value-barrier(%h is rw) { %h }
 return-value-barrier(%hash)foobar = OH NOES twice; say %hash
(foo = {bar = OH NOES twice}).hash

 # works, should:
 sub let-me-through(%h is rw) is rw { %h }
 let-me-through(%hash)foobar = 42 again; say %hash
(foo = {bar = 42 again}).hash

 # works, shouldn't:
 class NonLeaky { has %.h }
 my $instance = NonLeaky.new(:h(%hash))
 $instance.hfoobar = OH NOES thrice; say $instance.h
(foo = {bar = OH NOES thrice}).hash

 # works, should:
 %hashfoobar = 5; say %hash
(foo = {bar = 5}).hash

Note that there's only ever *one* hash of hashes here. No defensive
copying. Just references to the same one.

So you see, it's not so much the *object* having a
write-me-don't-write-me flag on it -- that simply won't fly. It's more
like were passing a reference around through various (routine or
object) boundaries, and each of these boundaries has the right to
deprive the reference of its writeability. And then, somehow, all
indexings into that same reference are affected too.

I know that the specifics of this aren't trivial -- and in fact, the
issues one runs into with this are likely exactly what Stefan suggests
will be worse than the original problem -- but at least this forms a
seemingly consistent model of how containers should work in order not
to be leaky. A model that we could use as a starting point for further
discussion and maybe even implementation.

So, my question is now: does this make sense? Objects don't have to be
defensively copied, but when passing through non-rw barriers they (and
all their indexing descendants) can be deprived of their writeability.

Some references. It all comes down, in the case of OO, to the object
being able to protect its invariants. Over at c2, some people question
whether this is always necessary.

 https://c2.com/cgi/wiki?ReturnNewObjectsFromAccessorMethods
 
http://stackoverflow.com/questions/926404/java-is-clone-really-ever-used-what-about-defensive-copying-in-getters-setter

// Carl


Re: Encapsulating the contents of container types

2011-09-09 Thread Moritz Lenz

Am 09.09.2011 12:44, schrieb Carl Mäsak:

Non-rw attributes, non-rw parameters, and non-rw return values all
share the same mechanism. Fine. Makes sense.

[...]

Non-rw-ness in this case should be all the way, not one level down.


(I hope I didn't change the meaning by stripping parts of your quote).

Woah there. Every routine that returns something mutable would have to 
indicate that in its signature, and there are a lot of routines that 
return mutable objects, or lists thereof (map, sort, grep, first, 
constructors of ref types, ...)


That would basically move Perl 6 to a level where you have to declare 
mutability in an almost Haskell-ish way.


I think we should consider the implications of 
immutability-all-the-way-down before we jump to any conclusions. In 
particular list cases where typical Perl 5 code fails when ported to Perl 6.


Basically any code that deals with file handles, database handles, 
statement handles etc. (all mutable objects) would need to take care 
while passing them around, because a single non-rw return would render 
them immutable, and thus useless.


I used to be in favor of the immutability approach, but the more I think 
about its effect on actual code I've written in the past, the more I get 
scared by the thought.


Cheers,
Moritz


Re: Encapsulating the contents of container types

2011-09-09 Thread Carl Mäsak
Moritz ():
 I think we should consider the implications of immutability-all-the-way-down
 before we jump to any conclusions. In particular list cases where typical
 Perl 5 code fails when ported to Perl 6.

Indeed. Clearly I'm missing at least one piece of the puzzle here.

Back to the drawing-board... :-)

// Carl