Feature request: With

BlitzMax Forums/BlitzMax Module Tweaks/Feature request: With

ImaginaryHuman(Posted 2007) [#1]
Just wondered how easy it would be to implement a `with` feature, like in Visual Basic, where instead of things like:
Instance.MyFunc1()
Instance.MyFunc2()
Instance.MyFunc3()
Instance.Hello=True
Instance.Method1()

You just say:
With Instance
   MyFunc1()
   MyFunc2()
   MyFunc3()
   Hello=True
   Method1()
End With

BlitzMax would store the `current instance` in some internal local variable and then refer to it each time for any functions or methods or fields etc within the type that you want to access. Surely this would be at least a little bit more efficent, and make for easier to read code?


impixi(Posted 2007) [#2]
Surely this would be at least a little bit more efficent, and make for easier to read code


I agree. Seems like a good Idea.


TaskMaster(Posted 2007) [#3]
Man, I would love to have that. I think about that all the time as I am writing code...


DJWoodgate(Posted 2007) [#4]
With Instance1
  With Instance2
   .Myfunction()
  End With
End With


Not really familiar with how other languages deal with this. I assume you would have a leading stop to indicate this function is with the named Instance rather than at global scope?

EDIT. Hang on I was getting confused.
So in that inner with we are refering to instance1.instance2.myfunction() ?
If you wanted to refer to an instance1.myfunction() in there you would leave off the leading stop and say instance1.myfunction() which would make it absolute. Hopefully. So what if you wanted the inner with to refer to a separate instance of Instance2, not Instance1.instance2. I suppose you could have With .instance2 to make it process it as instance1.instance2 and leave off the period to make it process Instance1 and Instance2 separately? Sounds like a plan...

Not sure that having context determined by a period is a good idea from a readability standpoint though.


H&K(Posted 2007) [#5]
Isnt this really an IDE improvement? (Or at the least a first pre-first pass) So if you could get one of the IDE writters to put it in.


grable(Posted 2007) [#6]
There are other languages that has this.
VBScript and Delphi notably.

But there are some problems with the construct, feks:
Type A
  Field Value
EndType

Type B
  Field Value
  Method Action( t:A)
    With t
       Value = Value
    EndWith
  EndMethod
EndType

Which one is which? and it gets even more complicated with sub with statements feks.
You could do as VBscript and use "." as a with accessor to solve that problem, but brakes up the syntax imho.

But i still like the construct, and feel its up to the programmer to use it properly ;)


H&K(Posted 2007) [#7]
Which one is which?

They are both t.Value, if you want one not to be you add self.


Gabriel(Posted 2007) [#8]
I agree with H&K. They're both t.value because t's scope was declared more recently. The entire method is within Self's scope ( obviously ) but you then explicity set another scope. So in the same way that a local variable in that position would take precedence over a global, so should whatever you set with "with". Although I do see that it's different and you *might* assume that the method would always give precedence to the "self" object, but I think it's inkeeping with the way scope works in general for that not to be the case.


Russell(Posted 2007) [#9]
Purebasic has this, too. If BM had macros, you could probably "fake it" pretty easily.

I wish there was a language out that had the best features of BM and PB ;) (PB's version 4 IDE is world-class). But there isn't, so that's why I have both... :D

Russell


tonyg(Posted 2007) [#10]
But there are some problems with the construct, feks:


sub with statements feks.



Ahhhhh. Father Jack, how I miss him.


ImaginaryHuman(Posted 2007) [#11]
It's not IDE-level at all. I wouldn't want the IDE to just substitute the items within the WITH block for their original drawn out names. The language itself would need to be modified so that it actually stores the instance you're working `with` in some local variable and then uses that to jump straight to the item or make assumptions that speed it up. There's no point it just doing a search-replace.


Koriolis(Posted 2007) [#12]
When they say IDE-level, they mean using a preprocessor that will do the modification behind your back, not that it should modify the source code you're typing.


ImaginaryHuman(Posted 2007) [#13]
That's the same thing and still missing the point. The point is not just to save time typing and to make your code more readable, it's to actually optimize computation by keeping the instance being referred to as an internal variable that is automatically assumed to be the base from which the various fields/functions/method are referenced, which is supposed to be faster than defining new base instances for every access. It removes the overhead. It has to be optimizing otherwise there is no benefit at execution time. So that's why it cannot be any kind of preprocessor thing.


H&K(Posted 2007) [#14]
I dont see how it would make the code faster, (I admit my knowlage of this is small), but I cannot see how my IDE-Level solution would result in slower code.
Maybe Im wrong, but I was under the impression that
Function AFun(AType)
    Atype->dostuff
EndFuction
was the same speed as
Method AMeth()
    Self->dostuff
EndMethod
Because the compiler was already clever enough to keep the instance being referred to as an internal referrence?
That is all the with would be doing is saveing you typeing. Otherwise why bother with more than one compilation pass?
I say again that I dont know diddly squat on how Bmax (or any compiler above the level of Z80), works.


Koriolis(Posted 2007) [#15]
Concerning speed, as H&K said.
The "internal variable" would be nothing else than a local variable, something you - and a preprocessor - can already declare.
But even if speed is not an issue, that is not to say that every feature a preprocessor can do should receive the "who cares, a preprocessor can do it" answer...
Many "minor" things are best to be done by the actual compiler. This feature is part of them. Because a preprocessor would have to actually mimic a fair part of the compiler's work.
Indeed, given that we wouldn't specify anymore the variable we're acting on, the proprocessor - to handle the "With" feature - would need to know a LOT of contextual information:
- local variables in the function/method
- global variables in the WHOLE program (including the ones imported from other modules)
- the fields of any type in the WHOLE program (including the ones imported from other modules) so that when "with" is used inside a method the preprocessor can retrieve the fields of type of the current type AND its potential base type(s), and determine if it's a field that is accessed.

So indeed, I personally think it's asking a lot to a preprocessor.
But then to ask such a feature to the compiler, it has to fit in what the creator(s) (here Mark) envision to be best for the language.


TaskMaster(Posted 2007) [#16]
I don't think it is quite that involved...

All the compiler would have to do is check if the variable/function is in the scope of the With'd object. if it is, then use it as such, if it isn't, then handle it the way it normally would.

Of course, I am no expert, in fact, I didn't even sleep at a Holiday Inn last night (a bit of American humor).


Koriolis(Posted 2007) [#17]
But how would it know that
the variable/function is in the scope of the With'd object
? The point of "With" is precisely to avoid explicitely giving the name of the variable you're acting upon. And you can't just put a "Local Instance:? = ?" at the top, because then "Instance" would hide any other variable of that same name in outer scopes. It has to be determined on a case by case basis when you're implicitely refering to the "Instance" of the With declaraiont, or to another one. That is, emulate what the compiler already know in order to stick another resolution rule (for "With") in the process. That's like with "Self" you know: when you're in a method and do "Hello=True", the compiler looks for "Hello" in the parameter's list. If not found it looks in the outer scope : the current type and its base types (or said differently, in the type of the implicit "Self" variable). And if not found yet, it then looks in the global scope. Making "with" work means adding a rule to look in the type of "Instance" before anything else.


Paul "Taiphoz"(Posted 2007) [#18]
I think an easier example would be..

Type player
End Type

Global Player1:player = New player

With Player1
  x=1 
  y=1
  level=1
  score=100
  gunlevel=99
end with


The diff is that the bottom part of the code would look like this.

  Player1.x=1 
  Player1.y=1
  Player1.level=1
  Player1.score=100
  Player1.gunlevel=99



H&K(Posted 2007) [#19]
need to know a LOT of contextual information:
- local variables in the function/method
- global variables in the WHOLE program (including the ones imported from other modules)
- the fields of any type in the WHOLE program (including the ones imported from other modules)
Well, mt IDE (BLIde), does know all of that already, so I dont see that as a problem. (In fact given that all you are asking for is that the ide knows the information needed for it to give that "Type mamber" frame, that all the IDEs give, I dont see the problem even less


ImaginaryHuman(Posted 2007) [#20]
Surely the compiler does not currently take into account that you might be referring to an instance on more than one line of your program, to be able to optimize. When you do Player1.x=1 it will think, ok, this is a new instance, go figure out where the instance is and then relatively jump to the Player1 field and set it to 1. Then it'll see the next line, Player1.y=1 as an entirely new command unrelated to the previous one, will AGAIN go and look up the instance and jump to the Y field relatively.

Then again, I suppose if you put Player1 into a local variable, all it would be doing is an indirect addressing, as for pointers e.g. Player1[xoffset]=1. After all it is only pointer manipulation. The thing is that a `With` would take your instance which is not likely to be in a Local variable if you have lots of them, and could be stored in some linked list in memory, and puts it into a local variable while you're planning to access it. I suppose really this is only to help with writing the program and not to do with compiling it.

That said, for example, if you use an array it has to go to the array object then jump to the memory where the data is stored and then jump to the offset. If you did:

With MyArray
    [15]=4
    [12]=10
    [41]=1
EndWith

surely it could then keep the handle of the memory where the data is stored, in an internal local variable, and use that for indirectly addressing the array elements. You would not be able to simulate that with a preprocessor unless you directly go into the array object, extract the memory pointer and do pointer access.

The same would apply to, like, a TPixmap. MyPixmap:TPixmap is not a pointer to memory containing a pixmap image, it's a pointer to a TPixmap type instance which contains a pointer to memory which contains an image. So if you did...
With MyPixmap
    LoadPixmap(url) ' no need to specify which pixmap object
    RGBA=ReadPixel(x,y) 'no need to say in which pixmap
EndWith

or with a bank...
With MyBank
    With BankPtr
        [12]=$FF00FFEE
        [16]=$FFEEFF00
    EndWith    
EndWith

It can keep the pointer to the TBank object internally rather than having to specify it every time you want to change contents. Then it can keep the memory pointer in an internal local (you could do this part yourself), and then you're just left with having to write what offsets contain what data.

Apart from readability and ease of programming I do think there would be speed benefits. Accessing a block of memory with direct pointer access is faster than accessing an array or a bank simply because each time to access them you have to jump via the type before you get to the memory space.


Koriolis(Posted 2007) [#21]
will AGAIN go and look up
There is NO look up at runtime. The function just holds a pointer to the object, it si simply dereferenced. Avoiding this dereferencing would mean having the object copyed on the stack, which is not particularly faster...
The thing is that a `With` would take your instance which is not likely to be in a Local variable if you have lots of them
the solution that was given to you is precisely to have the preprocessor declare a new local variable pointing to the instance that "With" relates to.

Well, mt IDE (BLIde), does know all of that already, so I dont see that as a problem. (In fact given that all you are asking for is that the ide knows the information needed for it to give that "Type mamber" frame, that all the IDEs give, I dont see the problem even less
I don't say it's not possible, just that it's unfair to toss a feature by saying it can be done by a preprocessor, especially when it demands so much to the preprocessor. And it just feels a bit dumb too to "have to" mimic the compiler.
I'm not saying it's not doable nor that a preprocessor isn't usefull. Actually I do have (half-)written a preprocessor that aims to parse the *entire* BltizMAx grammar and thus be able to do literally anything the compiler could do.


H&K(Posted 2007) [#22]
The main point I wanted to make, was that we have ONE person writing the Compiler, but the IDE is open source. So surly its more likly (Irrelevent of which is easier, (And I still dont think I agree with you)), to get a pre-compelation solution, than a compilation one


ziggy(Posted 2007) [#23]
The speed issue would come when using functions retouring types, as the function would be executed in every substitution.
with MyBaseClass.GetNextButton()
x = 100
y = 200
width = 200
height = 100
end with

With the substitution suggested, I'm afraid code like this one could produce a cpu issue (the function will be evaluated on every line). and if the function returns different object on every call, the code would not work at all. The only way would be to store it on a intermediate local variable (as stated before), but I think this would be better addressed by the compiler (IMHO).


TaskMaster(Posted 2007) [#24]
Even if it does not make the program any faster, and all the precompiler did was put the With'd variable back on the following lines, I would like the feature. It makes the code look better, easier to read, and easier to debug. It the difference between:

sometype.somesubtype.do()
sometype.somesubtype.xyz=10
SomFunc(sometype.somesubtype.x,sometype.somesubtype.y,sometype.somesubtype.z)

As opposed to seeing this in your code:

With sometype.somesubtype
.do()
.xyz=10
SomeFunc (.x, .y, .z)
End With

For me, it doesn't matter if the program runs faster or not, it just makes the code appear cleaner, faster to type, etc...