Multiple return values like in LUA for a game scripting language
Hi, I'm creating a scripting language specifically geared towards games, so my main goals are to make it fast so it doesn't slow down the game, powerful enough to modify some core game mechanics if needed, and at the same time I want it to present a kind of a simplified layer of functions, so that for example in-game cinematics are easy to script for someone with no programming experience.
So far it's looking really good, but after reading some stuff about LUA, I've been thinking that, since functions can accept a list of parameters, it makes sense that they return lists of return values as well (in most scripting/programming languages, functions return only one value; when they need to return more than one they use pointers and structures, when having a list of return values would be much simplier). My system is also designed in a way that this would not slow down the scripts at all, and would actually speed them up in some cases. I can think of a number of situations where this would be useful, but still I'm not totally convinced, since it means that the parser would need some serious modifications. It seems that many people in this forum are familiar with this feature of LUA (and possibly other scripting languages). Is it really worth it?
BTW if there's another feature you'd like to suggest, I'm open to new ideas.
[edited by - Jotaf on March 14, 2004 10:19:30 AM]
quote: Original post by JotafInteresting idea. Good luck and keep us posted.
Hi, I''m creating a scripting language specifically geared towards games, so my main goals are to make it fast so it doesn''t slow down the game, powerful enough to modify some core game mechanics if needed, and at the same time I want it to present a kind of a simplified layer of functions, so that for example in-game cinematics are easy to script for someone with no programming experience.
quote: So far it''s looking really good, but after reading some stuff about LUA, I''ve been thinking that, since functions can accept a list of parameters, it makes sense that they return lists of return values as well... Is it really worth it?It''s worth it, but before we continue let''s take a diversion through a discussion of type.
The typing system in a programming language can be broadly classified as either strong or weak, and either static or dynamic. Strong typing means that the type of an object can never be changed, while weak means the opposite; dynamic typing means that the access mechanism to an object is type-agnostic, while static typing means that the access type must match the object type. Examples: C is statically and weakly typed; an int value can only be accessed through an int variable, but the value can be converted to a char by typecasting. Python is dynamically and strongly typed; variables have no type (which allows you to reassign different types of values to the same variable), but the values they refer to do, and the type of the value can never be changed or reinterpreted - a new value type must be created. Perl, OTOH, lies somewhere in between; variables have type (scalar, list, hashtable), but of a totally different classification than values (string, int, float).
A key element of "scripting languages," as they''re commonly known, is higher-level type abstraction. Perl provides hashtables as a native type. Python provides dictionaries (same thing as hashtables), lists and tuples, which are what you want. Tuples are constant-sized lists (once they''ve constructed), and can be built on the fly using the comma syntax to full effect.
So, to answer your question about multiple-value return, use tuples. Study some Python code to see how they use tuples, then consider looking at the Python source itself to see how they implemented them.
http://www.cs.jhu.edu/plbook/ is quite an interesting read on higher-order language design (a very accessible intro to operational semantics & type systems - heavily ML oriented, but you''ve got to start somewhere ).
quote: Original post by Oluseyi
A key element of "scripting languages," as they''re commonly known, is higher-level type abstraction. Perl provides hashtables as a native type. Python provides dictionaries (same thing as hashtables), lists and tuples, which are what you want. Tuples are constant-sized lists (once they''ve constructed), and can be built on the fly using the comma syntax to full effect.
Are tuples a proper subtype of lists then? (To be fair, I think you ought to mention that Perl also provides arrays, which I would assume are the "same thing as" lists.)
quote: Original post by ZahlmanNo:
Are tuples a proper subtype of lists then?
>>> t = (1,2,3)>>> type(t)<type 'tuple'>>>> isinstance(t, list)False>>>
quote: (To be fair, I think you ought to mention that Perl also provides arrays, which I would assume are the "same thing as" lists.)
quote: From my previous post...I think I did.
Perl, OTOH, lies somewhere in between; variables have type (scalar, list, hashtable), but of a totally different classification than values (string, int, float).
[Edit: return for type(t) disappeared.]
[edited by - Oluseyi on March 15, 2004 8:53:51 AM]
quote: Original post by Zahlman
Are tuples a proper subtype of lists then?
No, but then what constitutes a ''proper'' subtype is rarely all that important in Python Tuples and lists both conform to the abstract notion of a sequence, which allows a certain set of operations to be performed on it.
[ MSVC Fixes | STL Docs | SDL | Game AI | Sockets | C++ Faq Lite | Boost
Asking Questions | Organising code files | My stuff | Tiny XML | STLPort]
Thanks for your replies everyone =)
Oluseyi, I searched a bit about tuples and yes that's what I'm aiming for. All the parameters that you pass to a function can be thought of as a tuple, or a struct in C++. This way, functions also return tuples, which can be assigned to different tuples or even individual variables. Some examples (I know that they don't exactly state how useful something like this would be =)
int ix
int iy
struct target(int x, int y)
target.x = ix //Nothing new here...
target.y = iy
ix, iy = 10, 20 //a list of values assigned to a list of variables
target = ix, iy //a list of values assigned to a struct (which is just a list of variables, in this case 2 ints)
ix, iy = target //the values of a struct assigned to a list of variables
target = getenemycoords() //the function returns 2 ints, which are assigned to the 2 ints of the struct
ix, iy = getenemycoords() //we can just as easily assign it to 2 seperate variables
player.moveto(ix, iy) //here, the function is passed 2 integer variables
player.moveto(target) //the struct works just like 2 ints
The "getenemycoords()" is a function but it could well be a property like "enemy.coords", which works just like if it was a function (returning a x and a y). BTW the syntax CAN look a lot more user-friendly (less like C++ =P ) but I don't wanna get into too much details.
Ok, that's the main idea. It can save a lot of typing and make the code look a lot cleaner (in this example, instead of having to pass the x and the y to the functions every time, you can just use "target" or whatever instead of the 2 ints). Besides, it blends in perfectly with functions returning lists of return values =)
JuNC thanks for the link, it had some pretty useful information about tuples, but most of it is stuff that I already know
Anyways (sorry for the long post), I ran into another problem. Not really a problem, it's more like a design issue. To keep it short... The way I've got it structured, it would be really easy if the assignments are done in reverse order. Like, if you have "x, y = a, b", it would do this:
1) Calculate the value of a
2) Calculate the value of b
3) Make y = b
4) Make x = a
You see, the assignment is done in reverse order. This is because the scripts are heavily dependant on a stack and there's really no easy way to fix it (I've been thinking about it for a long time). Ok, so maybe it's not that big of a problem: the order of consecutive assignments doesn't change anything, ever; and even if the order in which you calculate the values is important (for example, if calculating "a" would call a function that would change something in the world so "b" would have a different result), it still works that way.
But I was thinking about adding support for stuff like, a list of function calls seperated by commas, and then a list of parameters that are then distributed through these functions, like this: "function1, function2 (a, b)", which would be the same as "function1(a)" and then "function2(b)". I'm already doing that with the assignments so it would be really easy to do. But then there's the problem that the left side is in reverse order, so actually you'd have to type "function2, function1 (a, b)" to get the same result. It's probably not needed. It's just that if there wasn't that confusing reverse, it MIGHT be useful (I just don't know how =P but since it's sooo simple to do it...). Maybe I shouldn't even bother? OMG maybe I'm throwing in too much stupid features just because I can =/ Ok all the other stuff I've got IS useful (really ;P ) I'm just not sure about this one =)
[edited by - Jotaf on March 17, 2004 6:16:41 PM]
Oluseyi, I searched a bit about tuples and yes that's what I'm aiming for. All the parameters that you pass to a function can be thought of as a tuple, or a struct in C++. This way, functions also return tuples, which can be assigned to different tuples or even individual variables. Some examples (I know that they don't exactly state how useful something like this would be =)
int ix
int iy
struct target(int x, int y)
target.x = ix //Nothing new here...
target.y = iy
ix, iy = 10, 20 //a list of values assigned to a list of variables
target = ix, iy //a list of values assigned to a struct (which is just a list of variables, in this case 2 ints)
ix, iy = target //the values of a struct assigned to a list of variables
target = getenemycoords() //the function returns 2 ints, which are assigned to the 2 ints of the struct
ix, iy = getenemycoords() //we can just as easily assign it to 2 seperate variables
player.moveto(ix, iy) //here, the function is passed 2 integer variables
player.moveto(target) //the struct works just like 2 ints
The "getenemycoords()" is a function but it could well be a property like "enemy.coords", which works just like if it was a function (returning a x and a y). BTW the syntax CAN look a lot more user-friendly (less like C++ =P ) but I don't wanna get into too much details.
Ok, that's the main idea. It can save a lot of typing and make the code look a lot cleaner (in this example, instead of having to pass the x and the y to the functions every time, you can just use "target" or whatever instead of the 2 ints). Besides, it blends in perfectly with functions returning lists of return values =)
JuNC thanks for the link, it had some pretty useful information about tuples, but most of it is stuff that I already know
Anyways (sorry for the long post), I ran into another problem. Not really a problem, it's more like a design issue. To keep it short... The way I've got it structured, it would be really easy if the assignments are done in reverse order. Like, if you have "x, y = a, b", it would do this:
1) Calculate the value of a
2) Calculate the value of b
3) Make y = b
4) Make x = a
You see, the assignment is done in reverse order. This is because the scripts are heavily dependant on a stack and there's really no easy way to fix it (I've been thinking about it for a long time). Ok, so maybe it's not that big of a problem: the order of consecutive assignments doesn't change anything, ever; and even if the order in which you calculate the values is important (for example, if calculating "a" would call a function that would change something in the world so "b" would have a different result), it still works that way.
But I was thinking about adding support for stuff like, a list of function calls seperated by commas, and then a list of parameters that are then distributed through these functions, like this: "function1, function2 (a, b)", which would be the same as "function1(a)" and then "function2(b)". I'm already doing that with the assignments so it would be really easy to do. But then there's the problem that the left side is in reverse order, so actually you'd have to type "function2, function1 (a, b)" to get the same result. It's probably not needed. It's just that if there wasn't that confusing reverse, it MIGHT be useful (I just don't know how =P but since it's sooo simple to do it...). Maybe I shouldn't even bother? OMG maybe I'm throwing in too much stupid features just because I can =/ Ok all the other stuff I've got IS useful (really ;P ) I'm just not sure about this one =)
[edited by - Jotaf on March 17, 2004 6:16:41 PM]
quote: Original post by JotafGlad it helped. In Python the comma operator is thought of as packing, so a series of variable references separated by commas (without any larger context) are packed together into a tuple, and any tuple can be unpacked into a similar series (of the same size as the tuple).
Oluseyi, I searched a bit about tuples and yes that''s what I''m aiming for.
quote: The way I''ve got it structured, it would be really easy if the assignments are done in reverse order.Constraining language specification by implementation concerns is not a bad thing. There''s no point writing a specification that requires jumping through hoops while bending over backwards to implement. Simply define this behavior in your language spec.
Incidentally, the comma operator in C evaluates left-to-right, but returns the rightmost value:
a = obj.method1(), obj.method2();
Naturally, such code is stupid because it obfuscates intent, and because C''s lack of an unpacking mechanism makes it unintuitive. Consequently, the comma operator is most often used in contexts where a single expression is required but two are desired (eg for loops with multiple counters/indices).quote: But I was thinking about adding support for stuff like, a list of function calls seperated by commas, and then a list of parameters that are then distributed through these functions, like this: "function1, function2 (a, b)", which would be the same as "function1(a)" and then "function2(b)".I first tried to find a solution in an existing language, but I couldn''t find a simple one (ie, two standard library list-manipulation calls) and I had to ask myself what the purpose of this would be.
quote: OMG maybe I''m throwing in too much stupid features just because I can =/Couldn''t''ve said it better m''self.
You''re right, thanks =) It''s probably gonna be open-source, I''ll post in this forum when it''s done (it might take a while though, with school and other stuff I don''t really have much time for this).
quote: Original post by Oluseyiquote: (To be fair, I think you ought to mention that Perl also provides arrays, which I would assume are the "same thing as" lists.)quote: From my previous post...I think I did.
Perl, OTOH, lies somewhere in between; variables have type (scalar, list, hashtable), but of a totally different classification than values (string, int, float).
[Edit: return for type(t) disappeared.]
Oops, I was fixated on the other bit:
quote:
Perl provides hashtables as a native type. Python provides dictionaries (same thing as hashtables), lists...
^^;;
@Kylotan: Can you do something with ''isinstance'' or something similar to determine if a Python object implements this ''sequence'' interface (and sorry for importing language terminology from elsewhere)?
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement