ID:1482922
 
Applies to:DM Language
Status: Open

Issue hasn't been assigned a status value.

issettable proc


Format:
issettable(Var)

Args:
Var: The variable to test.


This returns 1 if the given variable can be set at runtime. Variables which are not const or read-only will return 1.


I think this proc would be useful as a safety check. It would have the opposite use that isnull() currently has, but would just be used less often. isnull() checks if a read value isn't safe for use in another proc, and issettable() would check if it's safe to try and overwrite a variable's value.

I looked into softcoding this proc, but it just doesn't seem possible to obtain this information, without actually generating a runtime error! The closest thing we have is the issaved() proc, but that doesn't differentiate between const and global or tmp vars, which are settable, so it really doesn't solve the problem.

Hopefully the functionality exists to allow this to be feasible.
isnull() only checks if a var is null or not.
In response to Lummox JR
Lummox JR wrote:
isnull() only checks if a var is null or not.

I know that. I was just saying that isnull() is commonly used for safety checking, since many functions just don't take null values as arguments, so in that way it can prevent errors from occurring.

Similarly, an issettable() proc would also prevent errors, especially in certain kinds of debugging libraries, where the author wouldn't know which vars would be defined as const in the various builds that it might be plugged into.

Wouldn't something like this make sense as a feature? As an alternative, you could have individual isconst(), isglobal(), and istmp() procs, which would make issaved() obsolete and allow us to gather even more information. In that case, I would assume isconst() would also cover any built-in read-only vars.

Basically, anything that would allow us to check if a var is constant or read-only at runtime would work. The nice thing about the individual procs is that you could easily combine them with && to get even more narrow results.
Well, while grabbing more metadata about variables is generally A+++, I'm actually wondering about the use-case?

BYOND doesn't really offer up a particularly robust reflection/RTTI equivalent as it goes, so I'm not entirely sure (very peculiar library design aside) how a library would get into a situation where it is setting a variable on an object passed in, knows the variable's name and semantic meaning, but doesn't know whether or not the variable is read only?

Is there a concrete example you've got at the moment, that you can't do due to the lack of runtime information?
In response to Stephen001
Stephen001 wrote:
BYOND doesn't really offer up a particularly robust reflection/RTTI equivalent as it goes, so I'm not entirely sure (very peculiar library design aside) how a library would get into a situation where it is setting a variable on an object passed in, knows the variable's name and semantic meaning, but doesn't know whether or not the variable is read only?

Well, it's actually quite easy for that to happen. All the library has to do is make use of an object's vars list, and then it knows the name and value of every single var contained within that object. The library might not immediately recognize the semantics of certain vars, at which point the user can supply it with that information through some sort of interface. Other procs such as isnum(), istext(), istype(), etc. would also be very useful.

It's too bad that DM doesn't provide more reflection, because it has become one of my favorite aspects of programming. There is just so much you can do with it, that many programmers don't seem to realize yet. Reflection can make software more organic and less mechanical. Theoretically, programs that make use of the reflective properties of a language could be vastly superior to those that don't. Reflection can give programs a kind of "self-awareness" that can make them inherently more intelligent and easier to work with. It can also greatly simplify any kind of automated process. I find these to be excellent attributes for any game development language, due to the very nature of game design.

Is there a concrete example you've got at the moment, that you can't do due to the lack of runtime information?

That's a good way to put it, but it's hard to say that I have something concrete because the absence of this feature is somewhat of a barrier to designing some very ... interesting features, which makes it hard to see what's on the other side. What I do know is that if this is implemented, it will allow me to gain full control over almost every aspect of the engine, so that I can move everything around as if it were a puppet. Add in a simple script parser, and who knows what you can make it do! Just include the library and you will instantly have every object instance at your command! You could do all kinds of designing, prototyping, and debugging at runtime before updating the source with the final values, then recompiling. This would open the door to a completely different paradigm.
The thing is, excepting serialisation/deserialisation of objects to other data formats, I don't see the use-case. Which isn't a /bad/ use-case, mind, but given the pretty blargh-tastic performance on world/Export(), and the fact issaved() actually probably does that use-case just fine ... it's not s strong case for adding such functionality.

I don't really buy the argument of the use of this, without a use-case to demonstrate it. And I don't really buy that crafting a use-case is very difficult because the functionality doesn't exist, when we're able to define the functionality in the first place.

There is also of course the concern that you get with reflection and RTTI in other languages, with use of vars as it is. Breaks encapsulation, meaning crafty libraries pulling things out from under you, giving rise to harder to diagnose bugs. Which in Java or C++ is bad enough, but in a learner language? Mmmm ...
In response to Stephen001
Stephen001 wrote:
The thing is, excepting serialisation/deserialisation of objects to other data formats, I don't see the use-case. Which isn't a /bad/ use-case, mind, but given the pretty blargh-tastic performance on world/Export(), and the fact issaved() actually probably does that use-case just fine ... it's not s strong case for adding such functionality.

The feature that I'm requesting really has nothing to do with world.Export() or issaved(), although I could see it being useful in saving things as well.

I don't really buy the argument of the use of this, without a use-case to demonstrate it. And I don't really buy that crafting a use-case is very difficult because the functionality doesn't exist, when we're able to define the functionality in the first place.

I actually haven't defined the functionality. I was only speaking of the basic primitive stuff that this would allow. I could build much larger things on top of that.

There is also of course the concern that you get with reflection and RTTI in other languages, with use of vars as it is. Breaks encapsulation, meaning crafty libraries pulling things out from under you, giving rise to harder to diagnose bugs. Which in Java or C++ is bad enough, but in a learner language? Mmmm ...

Encapsulation... you must know my enemy well! Encapsulation only needs to work under normal gameplay conditions. If you are debugging and developing the world, then encapsulation shouldn't apply to the developer's mob. Even though it's an object, that mob is the developer, so it should be granted full access to the world! I am talking about a completely different kind of scenario.


Also, I just happened to discover what appears to be a bug, very much related to this discussion. I have somehow exploited the vars list to successfully alter the value of a const var at runtime! Just to verify, this should never be possible, and so it would in fact be a bug, correct?
I didn't understand pretty much any of that (as in, I read the words, but couldn't construe a sensible meaning), so I'll address the bug.

Well, the vars list is just an associative list of the variables on an object. I wouldn't expect it to necessarily enforce const-ness. I mean, it would make sense if it did, in one interpretation (it's a view on a const variable), and make sense that it doesn't in another interpretation (it's an associative list entry and thus, settable).

This is kind of why the vars list is whacko. So ... maybe a bug, maybe not? Does it need fixing?
Shhh, don't tell Lummox JR. I actually kind of like this bug. It's worthy of the Red Book if it doesn't get smashed.

Here it is:
mob
var/const/completely_constant_right = "Hi, I'm a constant variable."

verb
Force_Change_Constant() //This totally works!
src << "Before Change: [completely_constant_right]"
for(var/v in vars)
if(vars[v] == completely_constant_right)
vars[v] = "I'm not so constant anymore!"
src << "After Change: [completely_constant_right]"

Isn't that funny? Instead of setting it like a normal var, I changed its value directly as a list item.
In response to Multiverse7
By the way, you can change vars using the vars list and the name of the variable as a string.
vars["completely_constant_right"] = etc.
In response to Kaiochao
That's right! I remember that when I was parsing the mouse params.

It's too bad we can't add any new vars at runtime, just by adding to the list:
vars["vars"] += list(new_variable = "some_value")
// Yes, vars somehow exists within itself infinitely,
// without causing a memory leak.
// This makes it through the compiler,
// but generates an error at runtime.

I suspect that in reality, an object's vars are really nothing more than associative list items within the vars list, and if that's true then adding new vars to an instance should be possible, but that's a whole other feature.
In response to Multiverse7
I don't think adding variables at run-time would be very useful. Your code ends up being less reliable since you would have to keep track of when a variable exists to be accessed. You're better off using your own associative list that isn't read-only.

Also, variables aren't contained in other variables. When you set a variable to another variable, you're really just setting the variable to the value of the other variable.
var some_list[1]
some_list[1] = some_list

// some_list[1] == some_list
// some_list[1][1] == some_list[1] == some_list

The code only needs to go through as many operations as you give it. If you tried to loop through some_list and through recursion loop through some_list[1] and some_list[1][1] and so on, then you would end up with a pretty obvious infinite loop, but the list object itself only has a length of 1.
In response to Kaiochao
That must mean that the very act of observing a list must itself be an operation.

What if the addition of new variables was limited to the creation of modified types? You could simply add the var keyword inside the formatting to create a new var that was not previously defined:
var/newobj = new /obj {var/newvar = "somevalue"}