Class Prototypes

In Javascript, objects and arrays have access via a member to their parent class’s prototype. This allows the user to modify a base property of the class and have it affect all instances. We can do something very similar and very easily in Copper.
Cutting straight to the code, suppose we have an object named Beast, which we copy (via assignment) to make multiple instances:

Beast = [ legs=4 ] { }
rover = Beast
spike = Beast

Both “rover” and “spike” here inherit the member “legs”, which is set to four. Now suppose “rover” loses a leg. We substract one.

–(rover.legs)
# Or rover.legs = 3 #

Then later on “rover” completely heals. How many legs should rover have? We could do…

rover.legs = Beast.legs

But we’re lazy, a real world scenario might be more complicated, and we want to use prototyping. With that in mind, we could set up Beast as follows:

Beast = [
legs = 4,
heals = { super.legs = super.prototype.legs }
] {
this.prototype ~ this,
ret(copy_of(this))
}

This does mean that we need to change our construction of “rover” and “spike” from being direct copies of Beast to being the recipients of function calls to Beast. This pattern may be comfortable to those who prefer constructors, and it allows for initialization parameters.

rover = Beast()
spike = Beast()

Alternatively, we could assign the prototype member of Beast after its construction.

Beast = [
legs = 4
prototype = {}
heals = { super.legs = super.prototype.legs }
]
Beast.prototype ~ Beast

In either case, now we can call the “heals” function on “rover” and let the prototype do the work.

rover.heals()

What about cyclic references?
Copper has no such issue since everything is tied to the global stack. If we were to replace the implementation of Beast with an empty function, all the pointer references would, upon being used, declare the presence of an empty function and terminate. No crash; just a warning message.

Replacing the Body of an Object Function

Suppose you have an object “a” that you have already instantiated members in.

a.b = 10

Now suppose you need to make the return of “a” equal to 9 so that:

print(a:)

… prints “9”. If you were to assign 9 to “a” directly, you would be giving “a” an entirely new object-function, thereby losing the member “b”. Fortunately, there is a way around the problem. Copper has a built-in function called “share_body” that allows two object-functions to share the same execution body. For example:

c = 9
share_body(c a)

… would allow “a” to have the body of “c”. If “c” gets destroyed from going out of scope, then “a” keeps the body. However, creating a new variable just to make a function body is unnecessary. If we create an ownerless object-function to share its body with “a”, then “a” will become the owner of the body.

share_body({9} a)

The ownerless object-function {9} is instantiated within the function-call parentheses of share_body where it dies but its body lives on. And there you have it, no copies required, no loss of object members.

Multidimensional List Solution

In a previous article, I wrote about how it was difficult to produce a 2D list in Copper due to the ownership dilemma. The solution at the time was to write a singly-linked list in Copper. It turns out, there is a better approach, one that will also allow us to create any multi-dimensional list.

Continue reading “Multidimensional List Solution”

Multidimensional List Access Problem

Suppose you wanted to create a 2-dimensional list. This can be easily done with the list constructor. ex:

mylist = list(list(9))

Now suppose we wanted to access the item in the second dimension of the list. Lists are special in that they are objects that contain other objects without themselves containing variables. To access an item in the list, we use the item_at() system function. This returns an object. Performing it once will return the child list. From there, we can call item_at() in succession to access the item of interest:

myitem = item_at(item_at(mylist: 0) 0)

Continue reading “Multidimensional List Access Problem”