[gameprogrammer] Re: More scripting: Multiple return values

  • From: David Olofson <david@xxxxxxxxxxx>
  • To: gameprogrammer@xxxxxxxxxxxxx
  • Date: Tue, 6 Apr 2004 19:36:38 +0200

On Tuesday 06 April 2004 17.31, Bob Pendleton wrote:
> On Tue, 2004-04-06 at 09:17, David Olofson wrote:
> > Some more questions regarding RT scripting language design;
> >
> > * Does anyone have experience with game and/or real time
> >   scripting with a language that support functions with
> >   multiple return values?
>
> No, not those specific kinds of languages, but I do have experience
> with languages that have multiple return values and have spent a
> lot of time looking at the problem of developing languages that
> support them.

Perfect! :-)


> > * Are multiple return values seriously useful? Confusing?
> >   Dangerous?
>
> They are rarely useful, very confusing, and slightly dangerous. It
> is hard for a programmer to keep track of what is being returned.
> That is especially true in languages with dynamic typing and a
> variable number of return values. If you have static typing then
> the compiler can check for type mismatches, which saves the
> programmer from errors.

I don't have static typing at all (yet), so it looks like I should be 
very careful with this stuff, or better, just drop it. Besides, 
though I haven't hacked enough code to get into trouble yet, I can 
see a number of ways to mess things up, and I have yet to find any 
*really* good reasons to support multiple return values at all.


> If you language has a vector data type then it is easy to have
> functions that return variable length vectors that contain the
> multiple values.

Actually, both arguments and return values are passed using low level 
light weight LISTs already. (They don't own or manage the referenced 
data, so they're not really useful as a language level type.)

There will be a real vector type eventually, possibly replacing the 
lightweight LIST type, so it seems logical to just use that instead 
when you *really* want multiple return values.


> I prefer to use sets with a known enumeration of
> values to handle returning multiple values. The enumeration
> determines the possible return values and the set, once returned
> can be asked for members by name. Members that are non-existent or
> have a NULL value (depends on how sets are implemented) were not
> returned. Or, you can a default value for each member of the set
> and have the function just set the values it wants. A function
> might look something like:
>
>       function point() returns [x = 0, y = 0, z = 0, tag = "nothing"]
>       {
>
>               return x=10, y=30, tag="upper left corner";
>       }
>
> But, then assignment gets nasty:
>
>       x, y, ..., tag = point();
>
> The values have to be assigned in order to match the value of
> return values of the function and you need a syntax to skip over
> values you don't want.

In my language, a list of comma separate expressions evaluates into a 
LIST with one lvalue for each expression. It would be easy enough to 
add a "void" keyword, and/or give an empty expression within a coma 
separated list the same behavior as ignoring the return from a 
function. (The caller allocates space for the return value, but 
forgets about it after the function returns.)


[...]
> > I currently have support for multiple return values. (Functions
> > have a list of "return arguments" before the function name in
> > declarations.) There is support for variable argument count, but
> > the result count is fixed. (Hardwired at compile time.)
> >
> > I also have these function references we've been discussing the
> > last few days, and obviously, these cause trouble since I can't
> > tell how many return values to make room for by looking at the
> > function declaration. :-/
>
> The reference points to the function. If you can call a function
> you have to at least know the number or arguments it takes.
> Therefore the reference must point to a function descriptor.
> Therefore you can expand the function descriptor to contain the
> full function signature including the return values so that it can
> be properly executed.

Yes. What I meant was just that with references (as opposed to 
constant/"hardwired" calls), you have to do all this at run time 
instead of compile time. Without statically typed function 
references, all I can do is have the "bytecode" and/or the VM's CALL 
instruction check that the called function is compatible with the 
provided arguments, and throw a runtime exception if it isn't.

Anyway, the problem with this in my current implementation is that I 
normally allocate space for return values at compile time, just like 
for expression evaluation and stuff like that. That is, the code 
relies on hardcoded register indices, so I can't just insert some 
variable size array in the middle and then go on executing VM. I'd 
have to put the variable size result list above the calling 
function's register frame or something.

Now, if a function can only ever return a single value, this problem 
goes away. :-)


> > A related problem is caused by the fact that I've implemented the
> > assignment operator ('=') as an actual operator. That means it's
> > handled by the normal infix expression evaluator, which in turn
> > means that I can't just look at the left hand subexpression to
> > figure out how many arguments I want, in case the right hand
> > subexpression happens to be a function call. (*If* that's
> > actually desired, that is.)
>
> Solved above. The assignment operator can inspect the function
> signature to determine what it is going to return.

Except that operators are only invoked *after* their arguments have 
been calculated... Function and operator arguments can only be passed 
by reference or by value. To get around that, I'd have to remove the 
"=" operator and replace it with a special grammar rule for 
assignments - which is probably just as well, as "=" is the only 
operator that treats the left hand term as write-only.

Anyway, no big deal. I just though it was a good idea to make "=" just 
another operator, but it seems like I was wrong.


> > As usual, it's probably a good idea to decide which problems to
> > solve before starting to solve them. ;-)
>
> In LISP... you just return a list of values if they are all the
> same type or you return an associative list (which is pretty much a
> set). Perl does it by returning vectors.

Yeah... I think I'll convert to that approach. Some form of automatic 
memory management and a nice vector construction syntax should do the 
job, and also happens to be really rather usefull for lots of other 
stuff.


//David Olofson - Programmer, Composer, Open Source Advocate

.- Audiality -----------------------------------------------.
|  Free/Open Source audio engine for games and multimedia.  |
| MIDI, modular synthesis, real time effects, scripting,... |
`-----------------------------------> http://audiality.org -'
   --- http://olofson.net --- http://www.reologica.se ---


Other related posts: