ID:2298164
 
Applies to:
Status: Open

Issue hasn't been assigned a status value.
Summary:
The 'with' keyword would be used as a means of accessing a datum or atom's members without needing to reference the object for every time you'd like to access. 'with' creates a scope similar to 'using' in C++ where the object's procedures and variables can be referenced anywhere in that scope.

The object's members take precedence over globally-scoped identifiers of the same names.

Another feature of 'with' is to be able to check if an object is not null before executing the instructions inside of the scope. If null, the scope is skipped.

Example:
proc/test_proc()
var/mob/m = new()
with (m)
loc = locate(1,1,1)
name = "Simpleton"
desc = "He just might save the world someday."


Equivalent code without 'with':
proc/test_proc()
var/mob/m = new()
m.loc = locate(1,1,1)
m.name = "Simpleton"
m.desc = "He just might save the world someday."


The behavior of src should be changed to point to the object which is handled inside of the scope as well, for instances or examples where file() would be needed.

with (file("text1.txt"))
src << "More text."
This is pretty cool.

I kind of which we had some means of defining more complex macros that can act as blocks.

var/list/_WITH_QUEUE = list()

#define BEGIN_WITH(ref) if((ref)) {_WITH_QUEUE += src; src = ref;
#define END_WITH src = _WITH_QUEUE[_WITH_QUEUE.len];_WITH_QUEUE.Cut(_WITH_QUEUE.len);}


Changing src has some side-effects though. Would be interesting to see how Lum approaches this.
++
I'm looking at this to see what might be doable, mostly as an exercise in learning more about the compiler. At first glance this looks quite difficult, but maybe there's a way.

The compiler is broken up into three steps: parsing, generation, and code output. Parsing is of course just converting the text into node structures that are usable down the line. Generation takes those nodes and does further work on them, and that's where stuff like the implicit src is added and var type paths are handled. But the easiest place to handle with() would be in code output, where it's too late.

It's clear to me I have to handle this in the generation step (it's still the blackboxiest part, but I've made strides). There's a function called FindVarNode() that I believe is where this has to be done. Already that code was recently altered to handle global.vars, so I think this is mostly a matter of looking to see if the currently traversed node is a with block.

Now that said, this is where it gets tricky. Currently FindVarNode() passes pack a value through a pointer that says if an implicit src should be added to the var path. But this would now have to pass back a node. And if that node is a function call or a list index operation, whose type cannot be determined, then a global var with the same name really ought to get precedence.

So it's complicated. Seems doable, but it'll be an adventure.
In python the with keyword is used as 'context management' (https://docs.python.org/3/reference/compound_stmts.html#with).
For example:

with open('filename.txt') as file:
do_things_with(file)


this will automatically handle closing/flushing/unlocking the file once the with block is completed. The concept has been extended to all sorts of things i.e. using a with block for handling mutexes (could also be a candidate for easily handling multithreading in dm if it's ever implemented). every 'contextmanager' object type in python has an overridable __enter__() and __exit__() method called when its context is entered/exited using the with keyword. __exit__() is called regardless of how the block is exited (i.e. break, continue, return).

imo something like that would be a much better and useful implementation of a with keyword than this which is essentially saving you writing a bunch of src. or m. or whatever object you're referring to and ultimately no real significant boon. as for skipping a block, a simple if check already suffices...
In response to Metamorphman
Where in DM would you use this, though? DM doesn't have file handlers to close, for one. It doesn't have much of anything persistent to deal with.

It'd basic be a scope guard at that point.
I think you focus too much on obscure features for developers and not enough on features that improve the player experience.
In response to Skull132
Skull132 wrote:
Where in DM would you use this, though? DM doesn't have file handlers to close, for one. It doesn't have much of anything persistent to deal with.

It'd basic be a scope guard at that point.

I think they were pointing out that the keyword had other uses in other languages.

"with" in python is basically "using" in real languages. But good on python for managing to make yet another thing more confusing than it needed to be by using a non-standard format.
In response to Skull132
Skull132 wrote:
Where in DM would you use this, though? DM doesn't have file handlers to close, for one. It doesn't have much of anything persistent to deal with.

DM does have file handles (file(), savefiles, zipfiles, databases, icons, sounds, etc. Their resource handling is done automatically when they enter/leave scope (not quite sure about zipfiles and databases though).

But that's beside the point. The example was meant to show how a 'with' keyword could (and already has been) put to more effective use than the original suggestion. It doesn't just have to be for resource handling.

There are plenty of possible use cases within DM. One that comes to mind off the top of my head is for handling control loops. e.g.:

// hypothetical built-in context datum
context
can_enter()
// called before the block is executed. returns true to allow execution to proceed.

enter()
// called at the start of the block before anything else is executed

exit()
// called when the block is exited regardless of how (return, continue, break, exception, etc)

control_gate
parent_type = /context
var/entered

can_enter()
return !entered

enter()
entered = 1

exit()
entered = 0

npc
var
target
control_gate/walk_lock = new

proc/walk_loop()
with(walk_lock) // optionally could provide an alias for the context datum itself using 'as'
// i.e. with(new/context/random_context) as context
spawn while(target && get_dist(target, src)>1)
step_towards(src, taget)
sleep(5)

client/Click(turf/destination)
.=..()
if(istype(destination))
mob.target = destination
mob.walk_loop()


If implemented correctly, a with keyword that gives you control over the execution its block like that would elegantly solve the need for hacky solutions such as this: http://www.byond.com/forum/?post=2285385

MrStonedOne wrote:
"with" in python is basically "using" in real languages. But good on python for managing to make yet another thing more confusing than it needed to be by using a non-standard format.

Spoken like a true ignorant asshole. 'using' in C++ and its cousins handles namespace unpacking and type aliasing. This is completely different to the problems python's with is solving (and very neatly with a great deal of flexibility at that imo). If you're going to be a dick about something at least try and have a clue what you're talking about. 5 minutes of reading could've saved you embarrassing yourself by talking out of your ass like that.
In response to Metamorphman
Metamorphman wrote:
MrStonedOne wrote:
"with" in python is basically "using" in real languages. But good on python for managing to make yet another thing more confusing than it needed to be by using a non-standard format.

Spoken like a true ignorant asshole. 'using' in C++ and its cousins handles namespace unpacking and type aliasing. This is completely different to the problems python's with is solving (and very neatly with a great deal of flexibility at that imo).

Why do you care if i'm being an asshole to python. You aren't python. There is really no need to take offence at what I said, It wasn't directed at you.

If you're going to be a dick about something at least try and have a clue what you're talking about. 5 minutes of reading could've saved you embarrassing yourself by talking out of your ass like that.

https://docs.microsoft.com/en-us/dotnet/csharp/ language-reference/keywords/using-statement

To paraphrase the great metamorphman: "If you are going to call somebody a dick at least try and have a clue what you're talking about. 5 minutes of reading could've saved you embarrassing yourself by talking out of your ass like that."
In response to MrStonedOne
MrStonedOne wrote:
Why do you care if i'm being an asshole to python. You aren't python. There is really no need to take offence at what I said, It wasn't directed at you.

I'm not exactly offended, it's just funny when someone like you shows up and shits on other things out of ignorance.

https://docs.microsoft.com/en-us/dotnet/csharp/ language-reference/keywords/using-statement

To paraphrase the great metamorphman: "If you are going to call somebody a dick at least try and have a clue what you're talking about. 5 minutes of reading could've saved you embarrassing yourself by talking out of your ass like that."

*shrug*

I did say 'C++ and cousins' not specifically 'C#'. If you knew more about 'real languages' maybe you could see that this particular version of using is totally different to the using statement in C++. But I guess being non-standard is ok when it's your preferred language doing it. Also, it's still completely different to how with works.

Also: https://docs.microsoft.com/en-us/dotnet/csharp/ language-reference/keywords/using-directive
In response to Metamorphman
*Cough* It's a complex scope guard (in C++ anyways), as I originally said. *Cough* http://www.boost.org/doc/libs/1_54_0/libs/scope_exit/doc/ html/index.html
https://rnestler.github.io/c-list-of-scopeguard.html

Semantics aside. I suppose if the choice was between a keyword with new scope that can just save you a few letters, vs something with actual logical consequence, perhaps the latter would be better. Would reduce the booleans of, "Am doing shit don't disturb" that pop up in processing objects.
Just for added context, C#'s syntax for the original proposal of this request is the following:
var thing = new FooBar()
{
Prop1 = value1;
Prop2 = value2;
};
var thing = new FooBar() {
Prop1 = value1;
Prop2 = value2;
};


FTFY
In response to MrStonedOne
Metamorphman wrote:
Spoken like a true ignorant asshole. 'using' in C++ and its cousins handles namespace unpacking and type aliasing. This is completely different to the problems python's with is solving (and very neatly with a great deal of flexibility at that imo). If you're going to be a dick about something at least try and have a clue what you're talking about.



PJB3005 wrote:
> var thing = new FooBar()
> {
> Prop1 = value1;
> Prop2 = value2;
> };
>

MrStonedOne wrote:
> var thing = new FooBar() {
> Prop1 = value1;
> Prop2 = value2;
> };
>



In response to MrStonedOne
MrStonedOne wrote:
> var thing = new FooBar() {
> Prop1 = value1;
> Prop2 = value2;
> };
>


FTFY

In response to Unwanted4Murder
Unwanted4Murder wrote:
MrStonedOne wrote:
> > var thing = new FooBar() {
> > Prop1 = value1;
> > Prop2 = value2;
> > };
> >


FTFY


I've added a small addendum with a small added request of changing the behavior of 'src' when inside of the scope.
ugh who would willingly use c# it makes me want to gouge my eyes out
var mob/m = new

with(m)
src << name
src << density
src << attack


Couldn't you just do something like #define
In the sense that it simply parses with(m) removing the line
but the value inside of () would be pasted before the first variables defined within with()?
Page: 1 2