[Perl 6 page]

Assignment

Contents

Introduction

Any software engineer, with experience in fairly ordinary programming languages, should have an intuative grasp of what "assignment" is, and this is tied in with the concept of what a "variable" is. This can be amusingly hard to get across to the first-timer, but once it is understood, it is such a fundamental concept that we just know what it means, without having to think about it too hard.

However, sometimes you ought to think about it. The concept, while still in essence an "assignment", does vary from language to language. I once had to misfortune to read (and modify) some C# code written by someone who was not at all used to that language, and it showed that the strong concept of value-based objects was carrying over from his C++ experience.

Perl 6 is supposed to avoid the value vs reference type problems of Java, but the issues are still there to work through. Perl 5 has reference assignment semantics, with a little-understood mechanism available for operator overloading to make it look and feel like value semantics for class types that mimic numeric "value" types. Perl 5 clearly has "references" as a fundamental type of scalar value, much like C has pointers. But Perl 6 does not deal with "references" explicitly! In order to figure out what assignment in Perl 6 means in the tricky cases people ask about, and to deal with references vs values, we need to define precisely what it means in the normal case.

What do the Synopses Say?

Assignment has Reference Semantics

simple assignment semantics

This is seemingly at odds with the idea that the use of = is simply a call to infix:<=> with no special rules. That is, it is an operator, not a control statement of some kind. You should be able to write your own operator that does the same kind of thing. Being called on the object of the left-hand-side, you might think it works like C++'s assignment operator. That is the natural way to write such a function, and you certainly could write functions like that. But C++'s assignment gives value semantics, and as we saw above, we clearly need reference semantics. To make sure you are clear on the difference, here is an ordinary method that provides value assignment semantics:

class Point {
    has Num $.x;
    has Num $.y;
    # other stuff ...
		
    method value-assignment (Point $self: Point $other --> Point)
     {
        $self!x = $other.x;
        $self!y = $other.y;
        return $self;
     }
 }

Here is a diagram showing the state of the Abstract Machine before and after calling $p1.value-assignment($p2);. The example purposfully uses only value types for the attributes, so it doesn't have to worry about further deep copying.

ILLUSTRATION

To write a function with the meaning of the built-in assignment operator, you need to operate on the container, not the value. But how do you do that, when the value is what is passed to the function? That question is the whole concept of "what is an lvalue". An lvalue is a value refered to in such a way that it can serve as the left hand side of an assignment! In Pascal and other languages you can specify pass-by-reference which gives deeper hooks into the original thing, in certain respects. Perl 6 has is ref for parameters, so let's go with that for the moment and then get back to the details.

our sub infix:<=> ($left is ref, $right)
 {
    VAR($left).STORE($right);
    return $left;
 }

The parameter passing in Perl 6 always works by aliasing the local variable's name with the actual parameter's container. Saying is ref doesn't change this! Parameters are always passed by sending the container (the Item in this case) by value, which has the effect of passing the stored object by reference. That is, you are always passing a pointer, or more accuratly, it is like a & parameter in C++ because the container is normally invisible, not explicit.

However, the default situation is to make the container read-only. So the is ref doesn't make the function grab more of the caller's representation, as in Pascal. Rather, it means you are allowed to modify it. The difference between is ref and is rw is another story.

Variables always work this way. The variable is bound to a container, and the container holds some number of values. For item variables—those using the $ sigil—the container is an Item object which holds exactly one value. More strikingly, operations on an item variable automatically dereference the item container and operate on the pointed-to object. So, the existance of the Item object is normally invisible.

To suppress the falling-through and really refer to the Item container, use the VAR macro. So, the listing above calls STORE on the Item. The members of an Item container have not been specified yet, but the design uses Perl 5's TIESCALAR as a starting point.

The Deep-Seated Desire for Value Assignment

Sometimes, you really do want to replace the contents of an object, rather than change the reference to a different object. As I related in the Introduction, I've seen code that really wanted to work that way, but the C# language is maddening in having some types where assignment works with value semantics and other types that assign with reference semantics. If you are used to value semantics, as with C++, having to replace the attributes one-by-one seems like something is seriously missing. Since the problem comes down to not alising an object from another part of the extended data structure, a more native design would be to use a deepcopy method followed by reference assignment.

It is concevable that you could overload infix:<=> on selected types to make assignment work the way you want. That is, you could do it the Java/C# way. But should you? Besides the pragmatic issue that dispatching assignment at run-time could seriously hinder the ability of the compiler to optimize the code, you don't want to immitate Java here. The design of Perl 6 strives to do a much better job addressing this issue than Java. So, by all means, write a mutator that replaces all the attributes of an object. Just don't call it infix:<=>.

Look at the organization of the testing operators. You have =:= for container identity, which matches the := or ::= used for container-level assignment (binding). You have === for object identity, which matches the normal = used for identity-level assignment. You also have the eqv operator for value identity. For comparing values, you use all the "wordy" operators: cmp, after, before, and eqv. So, it makes sence to use a "wordy" form for value assignment, such as an assign method.

After using :=, the =:= operator will return true, as the former manipulates the same level that the latter tests. Likewise, after using the normal =, then === returns true. Again, = operates on the same portion of the representation that === tests.

So, define the assign method to mutate the object so subsequent use of eqv on the same argument will be true.

The value-assignment method on Point illustrated above could simply be called assign, following a convention that any type that wants to provide whole-value assignment calls it by this name.

No details of the Perl 6 standard library have been designed yet. But, I propose that it use this convention of having an assign method where sensible, along with other nicities such as clone and deepcopy.

But, if you want to get a little fancier, try this:

sub infix:<⇐> ($left, $right)
is inline
 {
    return $left.assign($right);
 }

# Now you can use the arrow for value assignment
$x = $y;  # reference assignment
$x ⇐ $y;  # value assignment

The plain left-pointing arrow, , has precident in being used for assignment in early block-structured programming languages. However, many fonts render the arrow head as a speck, not easily readable as an arrow. The double arrow, , is more visble and has the visual harmony with the = sign.

How Special is Assignment?