-
Notifications
You must be signed in to change notification settings - Fork 1
Object Methods
An object in BType is much like a struct in other languages. From the computer's standpoint, there is no code associated with a particular object. That is, methods associated with an object exist separately from the object itself.
In BType, methods are statically defined. You cannot assign a value to a method on an object:
object foo {
myMethod() {}
}
var x = new foo();
x.myMethod = func() {}; // type error
var y = new foo();
x.myMethod = y.myMethod; // type error
This allows for a single version of each method to be stored for any particular object. Methods are rewritten and uplifted to the global scope so that they can be accessed directly and potentially inlined.
When a method is rewritten, an implicit parameter is added to the function:
object Point {
int:value;
setValue(int:x) {
self.value = x;
}
}
# rewritten as
object Point {
int:value;
}
func setValue(Point:self, int:x) { # name would be rewritten, too
self.value = x;
}
When a method is accessed with a member expression, a function reference should be created and a reference to the object instance should be bound to the first argument of the function. Consider the following:
# from the previous example:
Point:p = new Point();
p.setValue(10);
When compiled to JavaScript:
var p = null;
p = new Point();
(setValue.bind(null, p))(10);In simple cases like the example above, the compiler may choose a more optimal form that does not involve partial function application. For instance, when the member expression is the callee of a call expression, the bound reference may simply be passed directly:
setValue(p, 10);While methods cannot be modified on an object instance, they can be stored:
object Point {
int:value;
setValue(int:x) {
self.value = x;
}
}
var p = new Point();
var foo = p.setValue;
func doSet(int:newValue) {
foo(newValue);
}
export doSet;
When doSet is called, the value of value on p will be modified. This is because of the above-explained behavior. The compiled version of this would look somewhat similar to the following:
function Point() {
this.value = 0;
}
function setValue(ptr, x) {
ptr.value = x;
}
var p = null;
p = new Point();
var foo = p.setValue.bind(null, p);
function doSet(newValue) {
foo(newValue);
}Many other languages provide a this pointer to methods. In BType, an implicit self parameter is made available to methods and the object constructor:
object Foo {
int:x;
new() {
self.x = 123;
}
int:getX() {
return self.x;
}
}
There is nothing special about the self parameter, it simply holds a reference to the accessed object instance. The semantics are similar to that of a self parameter in Python.
If you would like to change the name of the self parameter for any reason, you can use the following syntax:
object Foo {
int:x;
new([Foo:this]) {
this.x = 123;
}
int:getX([Foo:that], bool:somethingElse) {
return that.x;
}
}
The type of the parameter that's defined must be the same as the object that the method is defined on.
References to methods are created at the time the method is referenced in the code. This behavior may be counterintuitive compared to the behavior of other languages.
object Foo {
bar() {
# ...
}
}
const x = new Foo();
# In this example, a new reference is taken each time.
x.bar == x.bar # false
# In this example, each refN variable contains a different
# reference.
const ref1 = x.bar;
const ref2 = x.bar;
ref1 == ref2 # false
# In this example, only one reference is taken. References
# are equal only to themselves (i.e., their own instance).
const method = x.bar;
method == method # true