Inconsistent morphability #1026

Util opened this Issue Nov 2, 2013 · 2 comments


None yet

3 participants

Util commented Nov 2, 2013

Reported by:

The basic built-in PMC classes have some behaviour whereby a PMC object can switch type with some freedom, but it's not consistent. The morphing seems to be based on a concept of the PMC object (really, its head) being a variable that can hold values of many types, with the assign_p_p op copying the value (necessarily including its type) from one variable to another. If that model were consistently followed, a PMC would be able to morph from any participating type to any other. In fact, only a few assignment combinations work like that, while others perform unrelated operations that modify the target, or generate exceptions.

Example of a few assignments:

$ cat t53.pir
.sub main :main
        $P0 = box 3
        $S0 = typeof $P0
        say $S0
        $P1 = box "foo"
        assign $P0, $P1
        $S0 = typeof $P0
        say $S0
        $P2 = box 5
        assign $P0, $P2
        $S0 = typeof $P0
        say $S0
        $P3 = new "ResizablePMCArray"
        assign $P0, $P3
        $S0 = typeof $P0
        say $S0
        $P4 = box 7
        assign $P0, $P4
        $S0 = typeof $P1
        say $S0
$ ./parrot t53.pir
Can't set self from this type
current instr.: 'main' pc 44 (t53.pir:18)

This sequence shows first that an Integer can morph into a String, following the untyped variable/assignment model. But a String won't morph back into an Integer; it turns into a string representation of the integer. So this morphability isn't a symmetric arrangement. The String will morph into a ResizablePMCArray, suggesting that ResizablePMCArray is a class of value that can appear in these morphable variables, but ResizablePMCArray then won't morph back into an Integer, rejecting the operation entirely.

If assign_p_p is to be a useful operator in the general case, it needs to have a consistent meaning. There are two main possibilities that could be used. Firstly, it could mean that the target is to be morphed to have identical content to the source operand. If that's the meaning, then such assignment should consistently work between all pairs of PMC classes that support it at all. (PMC classes that don't have cloneable content, or whose content isn't mutable, can't be the source or target of such an assignment.)

The other main possibility is that assignment means to modify the target, within its existing class, to reflect some aspect of the source. This is essentially a coercion operation, with the target operand being a more strictly typed variable. In this case it is sensible for the operations actually performed to be more diverse than with the untyped-variable model, but one wouldn't expect any morphing between PMC classes. The set of coercions that's possible can sensibly be asymmetric: for example, PMC classes whose content isn't mutable can be the source of a coercing assignment even though they can't be the destination of any.

As these two kinds of assignment are both somewhat useful, it may make more sense to support both, with separate opcodes and separate vtable entries.


Summary of my parrot 5.7.0 configuration:
  configdate='Sat Oct  5 12:42:43 2013 GMT'
    osname=linux, archname=x86_64-linux-gnu-thread-multi
Benabik commented Nov 2, 2013

I think PMC morphing is largely considered a design wart at this point. Value types should likely refuse to do assignment from other types or perform an implicit conversion.

rurban commented Mar 8, 2014

Yes, I agree and I explain the consistency:

  • assignment has it's use cases for native types (I,N,S) with automatic type casting,
  • and assignment between different PMC is done via assign_p_p (assign_pmc vtable),
    which calls the set_pmc vtable method, which may call the morph vtable methods (i.e. cast).
  • any PMC can specify a morph vtable method to allow custom type casting.

Current parrot's morph works only for classes, not objects. This is of course not enough.

regarding cast vs coercion:
morph or assign_pmc only supports type casting from the content of the source to the type of the target.
unmodifiable targets need to throw errors in assign_pmc.
morph returns a copy and assign_pmc assigns this copy. pmc_reuse already does these readonly checks.

core just does not define all possible and doable morph methods currently, and can
only morph from classes, but this is merely a lack of implementations or usefulness.

I totally disagree with benabik that morph is undesirable at all.
It is one of the most basic OO principles to be able to define how to coerce to one type
or override from another type.
Without this extendability and inheritance of pmc's is limited to set_pmc only.

morph needs to set the new value also, not just change the vtable of the object.
A user class has no other possibility to set the new values than by specifying
morph coercion rules, which are class specific. i.e. *Array => Integer: elements => get_integer

running morph behinds the scenes of P1 = P2 is like doing an implicit cast.
This makes only sense for primitive types.
We require explicit casting via morph P1, P2 for non-primitive types..

morph is broken btw, see GH #372 for the branch which fixes and extends it

@rurban rurban self-assigned this Mar 11, 2014
@rurban rurban added Sev-medium and removed Sev-low labels Mar 11, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment