Changes to the AGS Scripting language

Started by monkey0506, Thu 03/07/2014 21:58:10

Previous topic - Next topic

monkey0506

Double-post for an unrelated issue.

A thought just occurred to me here, but what should be the expected behavior if the user tries to create new instances of the built-in types? For example, the following code is now perfectly valid:

Code: ags
GUI *new_gui = new GUI;
Character *new_character = new Character;
Object *new_object = new Object;


Of course, while these are valid script objects, they're totally unusable. They all have invalid IDs, and other invalid read-only attributes. Attempting to display new_gui, for example, aborts with an error that an invalid GUI was specified. This makes sense of course, but should it really be deferred to a run-time crash to handle this?

Making matters even worse is the auto-pointer nature of the String class:

Code: ags
String s = new String;


May look perfectly fine, and it even compiles now! But when the run-time encounters this line, it crashes with an error message that a user object of 0 bytes was requested. This isn't very friendly.

We could just denote in the manual that the new keyword is not meant for any basic or built-in types, but nobody reads the manual anyway, so we'd end up with dozens of tech help threads due to run-time crashing.

It may actually prove useful to allow the dynamic creation of some of these built-in types, say, a temporary dummy character only needed in one room. However, this would require additional steps to ensure that these objects are created in a meaningful and useful state. Conversely, we may also need to have a way to disallow dynamic instanciation of certain types (for example, extra Game or Mouse objects wouldn't make sense).

Calin Leafshade

Game and Mouse should probably just be declared as static if they arent already.

monkey0506

Mouse.x and Mouse.y are non-static fields, only accessible through the mouse instance (hence my insistence that for the time being, the Mouse class not be used statically). Aside from that, the Game type only has static properties and methods, but therein is my point that creating instances of the type is useless.

Crimson Wizard

Quote from: monkey_05_06 on Thu 03/07/2014 21:58:10
A thought just occurred to me here, but what should be the expected behavior if the user tries to create new instances of the built-in types?

This is indeed important.

We need to diverse classes that may be created by user (with new) and those that cannot be. It is hypothetically possible that some of the game objects (characters etc) will be dynamically created in future versions of AGS, but so far they cannot be.
And there are some classes that can be created dynamically, but this is performed by special methods like string creation and static functions (DateTime.Now). This is also possible that we may change this in future, but for now it should stay the same.

Trying to use "new" keyword with these types must cause compilation error.

I propose adding a keyword to define classes that cannot be created with "new". This could be "builtin", or "internal". Maybe someone has better name?

Gurok

#4
Should we also use this keyword to prevent people from creating structs that extend built-in types? Right now (3.3.0) it's possible to do:

Code: ags
struct ExtendedCharacter extends Character
{
}


which gives you a struct where every instance maps to character[0].

If I had to choose one your suggestions, CW, I would go with "builtin". "internalstring" is already a modifier, so "internal" seems bad.

But...

I was thinking we could do this with two struct keywords, "abstract" and "sealed".

"abstract" could be used to prevent people from instantiating your struct directly (only through derived structs). The engine would be exempt from this rule, of course.

"sealed" would prevent the extends keyword from being used. Extender methods would still be allowed, making the term "sealed" a little shaky, but I think they're pretty good fits.

I know you don't typically see "abstract sealed" in the wild, but there's also typically no completely inaccessible object construction like this. MSDN says that abstract and sealed can't be combined in C# because they're contradictory. That's not entirely true when they're used as modifiers for a class. They would just result in relatively useless classes (normally).
[img]http://7d4iqnx.gif;rWRLUuw.gi

SpeechCenter

Given the many additions to the language specification, I suggest you keep a list of those changes either in the original post or a separate wiki page so it's easier to go through the final result.
It's also important to review those final decisions to ensure future compatibility.

By the way, I would still consider changing the classes to static to indicate classes that cannot be instantiated, assuming backwards compatibility can be maintained. Hence it would allow using a static class modifier in the language.

monkey0506

Quote from: SpeechCenter on Fri 04/07/2014 04:49:55By the way, I would still consider changing the classes to static to indicate classes that cannot be instantiated, assuming backwards compatibility can be maintained. Hence it would allow using a static class modifier in the language.

I definitely agree that since we already have a static keyword, that it makes sense to reuse it to allow pure static classes, like Game, Room, and System (and Mouse too, if we could get proper X and Y static attributes to replace the instance fields -- though this would break basically every active AGS project's scripts over a capitalization issue :-\).

Crimson Wizard

#7
Quote from: SpeechCenter on Fri 04/07/2014 04:49:55
Given the many additions to the language specification, I suggest you keep a list of those changes either in the original post or a separate wiki page so it's easier to go through the final result.
We keep a list of changes in the form of Git history :). I used that for preparing change list for 3.3.0.


Quote from: Gurok on Fri 04/07/2014 01:11:38
"abstract" could be used to prevent people from instantiating your struct directly (only through derived structs). The engine would be exempt from this rule, of course.
I don't know... this will look weird. "Abstract" means something not real... its like using word "interface" for something what is not.

Gurok

Quote from: Crimson Wizard on Fri 04/07/2014 08:34:55
Quote from: SpeechCenter on Fri 04/07/2014 04:49:55
Given the many additions to the language specification, I suggest you keep a list of those changes either in the original post or a separate wiki page so it's easier to go through the final result.
We keep a list of changes in the form of Git history :). I used that for preparing change list for 3.3.0.


Quote from: Gurok on Fri 04/07/2014 01:11:38
"abstract" could be used to prevent people from instantiating your struct directly (only through derived structs). The engine would be exempt from this rule, of course.
I don't know... this will look weird. "Abstract" means something not real... its like using word "interface" for something what is not.
E: On other hand, it is a class which is implemented in the engine, and not in script, which means that its implementation is separate. So... okay.

I was working from this http://msdn.microsoft.com/en-us/library/sf985hc5.aspx

I think "abstract" on structs would allow us to optionally have abstract methods down the track.

If you don't like "abstract", I think "builtin" is the next best suggestion. Static only works for things like Game. Character could not have a static modifier, because technically there are instances, just not user-creatable ones.
[img]http://7d4iqnx.gif;rWRLUuw.gi

Crimson Wizard

I added this to previous post, but will re-post just in case:

E: (regarding using "abstract") On other hand, it is a class which is implemented in the engine, and not in script, which means that its implementation is separate. So... okay.

Calin Leafshade

I dont think you should use abstract because it has a real meaning in the paradigm and appropriating that for something unrelated would be bad.

Characters are *not* abstract classes.

I strongly suggest "builtin".

Crimson Wizard

#11
Quote from: Calin Leafshade on Fri 04/07/2014 11:22:01
Characters are *not* abstract classes.
I think they are, because they are not implemented in script.
We may say that there are Script character class (abstract, base) and its implementation in the engine (derived class).

E: On other hand, using "sealed" is ideologically incorrect, because Engine still derives an implementation from them...

Ok, I am still unsure :). I will think more about this. Also let's see if there are more opinions.

Calin Leafshade

To me, an abstract class is a class that cannot have an instance of itself. There are clearly instances of characters even if the scripter cannot make one.

Abstracts are designed to be subclassed.

Snarky

If the engine does in fact use a subclass, then abstract might be appropriate. But then again it couldn't be sealed, as you say, CW... And if the class (err, or struct?) can in fact be instantiated by the engine, it's not abstract. What you need to make this work is a way to distinguish instantiation in engine code from attempts in user script.

It wouldn't be possible to simply limit the scope of the constructor (and make the class sealed/final), so it can be accessed by the engine but not the user script (cf. Java package scope)?

Otherwise, I think you'll have to make up a non-standard keyword. builtin, internal... it doesn't really matter as long as it doesn't conflict with anything else. Will the keyword even be seen by users, or only engine devs?

Gurok

Hrmm... abstract classes can't be instantiated directly (e.g. new Character()). There's no rule about never having an instance of one. The idea here is that the user sees the abstract class (Character) and the engine is providing a phony implementing class that it constructs.
[img]http://7d4iqnx.gif;rWRLUuw.gi

Gurok

#15
Quote from: Snarky on Fri 04/07/2014 12:08:21
It wouldn't be possible to simply limit the scope of the constructor (and make the class sealed/final), so it can be accessed by the engine but not the user script (cf. Java package scope)?

Actually, I thought about having protected constructors for the built-in types. This doesn't solve the problem of another class using extender methods on a built-in class though.

Sorry for the double post.

Edit: "internal" might be used by C# so, semi-standard.
[img]http://7d4iqnx.gif;rWRLUuw.gi

Calin Leafshade

internal is a good candidate.

In C# internal members/classes are only accessible from within an assembly. If one could consider the engine and script as separate "assemblies" then to say that a character is an internal class makes sense I think even if it's not exactly inline with the C# definition.

I think we will, at some point, make it possible to instantiate characters from within the script (we're nearly there anyway) so it's not a huge issue either way but i think internal is a very good fit.

Snarky

#17
Quote from: Gurok on Fri 04/07/2014 12:10:57This doesn't solve the problem of another class extending a built-in class though.

It would if you made the class sealed/final. :P

Yeah, I'm more and more convinced that what's needed is a notion of horizontal scope, an access modifier that doesn't depend on class structure but on the location of the code, like "internal" in C# or the default/package scope in Java.

Edit: The alternative would be to not worry about it and just prepare to point and laugh at the idiots who try to instantiate internal classes.

Gurok

Quote from: Snarky on Fri 04/07/2014 12:22:16
Quote from: Gurok on Fri 04/07/2014 12:10:57This doesn't solve the problem of another class extending a built-in class though.

It would if you made the class sealed/final. :P

Yeah, I'm more and more convinced that what's needed is a notion of horizontal scope, an access modifier that doesn't depend on class structure but on the location of the code, like "internal" in C# or the default/package scope in Java.

Yeah, sorry, Snarky. I edited that. I meant to say using extender methods. If you use an extender method, you have that protected scope and so could call the constructor.

I can code up "internal" or "builtin". It's no problem. I just thought the two-word combo had more utility.
[img]http://7d4iqnx.gif;rWRLUuw.gi

Snarky


SMF spam blocked by CleanTalk