Pass by Value

One language question designers need to address is whether objects are passed by value or by reference when invoking callable objects. It’s common in object-oriented languages to pass by reference to avoid having to build copies of passed objects.

BOOL uses pass by reference, which begs two questions: Do we provide a means to pass by value when desired? And if so, which side — caller or callee — can do the magic?

A caller can always make a copy of an object before making the call. On the other hand, a callee can always make a copy of a parameter. The only distinction might be which is more cumbersome to do the usual way. That might argue in favor of that side getting any syntactical magic.

Another angle might be trying to determine with side is more likely to recognize the need for working on a copy of a parameter. If there were a significant difference, that argues strongly in favor of that side  having access to any magic.

I’ve decided (as a working theory to be analyzed and tested) that any special syntactical magic should happen in the callee. In BOOL, that means in the Action.

So for consideration, the following syntax for Actions:

@@Foobar
>>  *int  a = a
>>  *int  b = b
<<  *int  c = 0
.   *int  d = 1
.

The new magic is the use of an initializer list for passed parameters where the initializing object is the parameter. The implication is using the passed parameter to initialize a new copy of itself.

The initializer list for return objects (such as ‘c‘) or local objects (such as ‘d‘) may not contain a self-reference. That generates a compile error.

Currently passed parameters cannot use an initializer list with anything other than a self-reference. Since there’s no way (yet!) to have optional parameters, there seems no point in supporting pre-initialization.

That dual weirdness makes the syntax a bit ugly, which is one count against it.

The obvious way for an Action to work with copies of passed values is this:

@@Foobar
>>  *int  _a
>>  *int  _b
<<  *int  c = 0
.   *int  a = _a
.   *int  b = _b
.

Which isn’t the worst thing in the world.

Under the hood, the new syntax causes the compiler to emit an OBJ object rather than a REF object. The _a and _b parameters above compile to:

[0000] (REF, T_int, dkey, NULL, ('name','_a'))
[0001] (REF, T_int, dkey, NULL, ('name','_b'))

Whereas the a and b parameters from the top example compile to:

[0000] (REF, T_int, dkey, NULL)
[0001] (OBJ, T_int, dkey, @0000, ('name','a'))
[0002] (REF, T_int, dkey, NULL)
[0003] (OBJ, T_int, dkey, @0002, ('name','b'))

The rationale isn’t that bad, actually. The presence of an initializer list on a formal parameter — normally illegal — triggers a (legal!) compile change if the initialization value refers to the same object. It generates two objects from the syntax rather than one.

The first represents the input parameter; the second represents the local copy. Effectively, the syntax magic does what the obvious version in the second example does.

A caller wanting to pass a copy of an object needs to do it the obvious way:

.
.   *int  _a = a
.   *int  _b = a
.   @Foobar _a _b
.

Which I think is fine (declaration of intent and such).

All-in-all, I’m basically happy with the idea. But then I really liked using all those semi-colons at first, too. Time will be the ultimate test!

Advertisements
%d bloggers like this: