[pypy-dev] "Unwrap" Concidered Harmful

Rocco Moretti roccomoretti at netscape.net
Sat Sep 13 20:24:51 CEST 2003

Armin Rigo <arigo at tunes.org> wrote:

>On Sat, Aug 23, 2003 at 09:00:58PM -0400, Rocco Moretti wrote:

>> all information about value. So there is no way we can say that
>> s.unwrap(s.wrap(x)) == x.
>I am now coming closer to the idea that wrap() should be redefined as well,
>because it is used not only on simple types but also on "internal interpreter
>structures", like code objects plus others more internals than that.

Let me see if I can clarify my present understanding. (Thinking out loud follows...)

When we use wrap(x), we mean one of two things:

1) Give me an application level object that is equivalent to x.
2) Make the interpreter level object x available at application level.

Case 1

Michael advocates calling use 1) "build". For the build case, it is
wrong to assume that unwrap(wrap(x)) == x, as, in general, an
application level object may not have a 1-to-1 correspondence with an
interpreter level object.

My opinion is that "unbuild" should not exist. Once an object is built,
there is no guarantee that it will ever again correspond directly to
an interpreter level object. If we need an interpreter level object out
of an application level one, we should do it in a very restricted way,
for a specified purpose. If we want an interpreter level object, it is
so that we can do manipulations on it, but in general, unwrap and
manipulations are not associative: 
    space.unwrap(space.str(w_x)) != str(space.unwrap(w_x))
space.is_true() is an example of such a limited use function. In my mind,
is_true() exists for a single purpose - to determine if a wrapped
object is true so that the interpreter can determine if a
JUMP_IF_TRUE type branch should be taken. Any other use would be
considered a "hack".

I can perhaps see the need for a .to_string(x) function, with the
semantics that "the results of this function are going to be presented
directly to the user - I don't care what sort of bizarre and twisted
data structure 'x' actually represents, just make a reasonable string
so that the human at the keyboard has some kind of feedback for
debugging purposes." Use of the output for any other purpose would
be highly discouraged. - I would hesitate to add any more such functions
until a compelling need for one is demonstrated.

Case 2

This is what we are looking for when we say .wrap(frame). Here the role
of unwrap depends on how wrap behaves. [If unwrap_2 exists, it should
*always* have the semantics that unwrap(wrap(x)) == x] - Possible
alternative names for this functionality include shadow() or proxy().

As I interpret it, Armin indicates wrap_2() should create an application
level object that shadows an interpreter level object.

That is:
    y = space.wrap(x)
    print x # <'xyz'>
    space.fubar(y) #.fubar alters y
    print x # <'yzw'>
and also:
    y = space.wrap(x)
    print y # <'xyz'>
    print y # <'npq'>

In implementation, y is just a proxy which passes along the
modifications or attribute acesses to the underlying interpreter object,
and (unwrap(wrap(x)) is x) is True.

An alternative is to have wrap_2() create a new object which retains
enough information to recreate an interpreter level object when unwrap()
is called. In this case there would be no differences in output for the
print statements above, and (unwrap(wrap(x)) is not x) is True.

We still have a problem with unwrap if we allow the application level
to modify objects produced by wrap_2. Say we have a function object.
What happens when someone says:

    fun.func_name = name

in application level code? What if name is generated from the application
level and contains an object that represents "92% chance of the string
'MyFun', 7% chance of a class with the .__str__ method hooked up to the
fortunes generator, and 1% chance of rain."?

The only solution I see is to limit the rvalue to objects that were
also created with wrap_2 and thus have their interpreter level values
still attached. So in effect, our black box objects become a black hole -
values go in, but can never come back out to interpreter level.


P.S. On further consideration, there is a third solution. We *can* allow
an "unbuild" function, with the directive that it is nervous, and
raises an exception every time it is the least bit confused at what to
do. This allows for free flow of information in the regular cases when
we aren't doing anything tricky with the object spaces.

My concern would be that we would become dependant on a well behaved
unbuild, and require it in a critical piece of code, making development
of the clever object spaces difficult to impossible. I'd disapprove of
it's use in any code that would be exercised in interpreting the majority
of Python scripts. Discourage its use and save it for implementing the
dusty introspection/self referential corners of Python only - and be
sure to document the expected behavior of the return values well, so
any conflicts can be anticipated.

McAfee VirusScan Online from the Netscape Network.
Comprehensive protection for your entire computer. Get your free trial today!

Get AOL Instant Messenger 5.1 free of charge.  Download Now!

More information about the Pypy-dev mailing list