AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Unexpected Result (left to right paramater passing)
Goto page Previous  1, 2, 3, 4, 5  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Bug Reports
View previous topic :: View next topic  
Author Message
Xander



Joined: 23 Sep 2007
Posts: 142

PostPosted: Fri Sep 28, 2007 5:55 pm    Post subject: Reply with quote

lexikos wrote:
I think it would be more accurate to say that:
  • AHK fully evaluates each expression, pushing each result onto the stack,
  • then assigns these results to the parameters (represented by variables) in right-to-left order (i.e. starting with the value most recently pushed onto the stack.)
The issue is that an expression consisting solely of a variable name results in a reference to - not the value of - the variable. (Actually, the same applies to assignments, like index+=1.)

Yes, that is quite correct. A simple demonstration is as follows:
Code:
index = 1
MsgBox(index, index += 1, index++, ++index)
MsgBox(a, b, c, d) {
   MsgBox, % a " " b " " c " " d
}

Which gives 4 4 2 4
So in essence, it is storing the memory address of what is returned and not the value, and then assigning what is at each memory address after all expressions are evaluated.
Back to top
View user's profile Send private message
Xander



Joined: 23 Sep 2007
Posts: 142

PostPosted: Fri Sep 28, 2007 6:23 pm    Post subject: Reply with quote

Laszlo wrote:
Why is relying on side effects in function parameters is bad style?
- It makes the code non-portable, hard to understand and document, difficult to optimize
- It could make the compiler/interpreter slower or larger.

It may require a little more work to port, but it doesn't render it entirely impossible. I'll have to just take your word for it that it is hard to understand a list of left to right expressions. And can you be more specific about how optimization is done currently that would have to be abandon if left to right evaluation was guaranteed?

Laszlo wrote:
If a piece of code does not rely on side effects, any parameter evaluation order gives the same result. Good programming style does not depend on parameter evaluation order.

As you may or may not be aware, global variables are also very bad programming, especially temporary variables! They can introduce practically unreproduceable bugs which are the most frustrating. That's one reason why I avoid temporary variables and ONLY use them as local variables in functions (this too can be problematic in AHK as variables are not limited by their scope or "block". To illustrate a problem with using temporary variables consider:
Code:

ATimerLabel:
   Loop, 100 {
      temp := 100 - A_Index
      func(temp)
   }
   return
F1::
   temp := func2()
   Send, %temp%
   return

If the F1 hotkey interrupts the Timer after it sets temp but before it sends it to func, then when ATimerLabel is resumed, temp will no longer be what it should be.

So as you can see, there is a very good reason to use expressions in the parameter list as opposed to assigning them to temporary variables and then passing them to the function.

Laszlo wrote:
Why is requesting a specific parameter evaluation order possibly too limiting?

- There are languages, where you can enter an expression as a parameter, and it will be newly evaluated each time it is used inside the function.

Now that would break a lot of scripts if it were changed to be like that !

I can see the use of such a feature, but it should be implemented in such a way that won't break code. Such as requireing an = sign before the parameter ( i.e func(=x++,=--y) ). So no, I am not buying your argument.

Laszlo wrote:

- Lazy evaluation could prevent run-time errors. Like this:
Code:
Select(x,sqrt(x),1/x,0)
Select(x,a,b,c) {
   Return x > 0 ? a : (x < 0 ? b : c)
}
It will not get in trouble with sqrt(-1) or 1/0.

Those could use the = syntax as well could they not. But I do agree, it could be very useful. But I don't know how complex it would be to implement and if that complexity would be warrented. Either way, fixed evaluation order does not limit this capability as you propose.
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 1281

PostPosted: Sat Sep 29, 2007 12:01 am    Post subject: Reply with quote

Xander wrote:
Code:
index = 1
MsgBox(index, index += 1, index++, ++index)
MsgBox(a, b, c, d) {
   MsgBox, % a " " b " " c " " d
}

Which gives 4 4 2 4
So in essence, it is storing the memory address of what is returned and not the value, and then assigning what is at each memory address after all expressions are evaluated.

This result is rather surprising. Looks like in the case of index++, dereferencing of the variable is done, then increments the variable.
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 1281

PostPosted: Sat Sep 29, 2007 12:13 am    Post subject: Reply with quote

lexikos wrote:
  • AHK fully evaluates each expression, pushing each result onto the stack,
  • then assigns these results to the parameters (represented by variables) in right-to-left order (i.e. starting with the value most recently pushed onto the stack.)

Are you sure on this? Frankly, I'm doubtful on this. The stack is precious, and if the values which are of variable lengths are pushed on the stack, how could the called function know how much bytes should be popped/retrieved?
Although I can think of a way in AHK case, it's too troublesome and I don't think it's worth the effort.
And what about ByRef parameters?
Back to top
View user's profile Send private message
ahklerner



Joined: 26 Jun 2006
Posts: 1149
Location: USA

PostPosted: Sat Sep 29, 2007 1:34 am    Post subject: Reply with quote

Xander wrote:

....To illustrate a problem with using temporary variables consider:
Code:
ATimerLabel:
   Loop, 100 {
      temp := 100 - A_Index
      func(temp)
   }
   return
F1::
   temp := func2()
   Send, %temp%
   return


That code won't work on my machine Crying or Very sad
Quote:
---------------------------
test.ahk
---------------------------
Error: Call to nonexistent function.

Specifically: func(temp)

Line#
003: Loop,100
003: {
004: temp := 100 - A_Index
---> 005: func(temp)
006: }
007: Return
008: Return
009: temp := func2()
010: Send,%temp%
011: Return
011: Exit

The program will exit.
---------------------------
OK
---------------------------

_________________
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2494
Location: Australia, Qld

PostPosted: Sat Sep 29, 2007 3:10 am    Post subject: Reply with quote

Sean wrote:
Looks like in the case of index++, dereferencing of the variable is done, then increments the variable.
Yes, that is necessary since post-increment returns the previous value of the variable. I think that every operation pushes its result onto the stack. However, any assignment operation (except post++) results in a reference to the variable itself.
the manual wrote:
If an assignment is used as the input for some other operator, its value is the variable itself. For example, the expression (Var+=2) > 50 is true if the newly-increased value in Var is greater than 50. This also allows an assignment to be passed ByRef, or its address taken; for example: &(x:="abc").

Sean wrote:
Are you sure on this?
Not entirely certain. I am certain that all parameters are popped off the stack and assigned to the function's variables (literally) all at once (by Line::ExpandExpression(), before the function is called.) For this to happen, they must have all been evaluated and pushed onto the stack beforehand.
Quote:
Frankly, I'm doubtful on this. The stack is precious, and if the values which are of variable lengths are pushed on the stack, how could the called function know how much bytes should be popped/retrieved?
The called function doesn't use the stack - it uses (quite literally) variables which represent the parameters. Also, the stack is an array of (pointers to) tokens, so it doesn't matter how large each value is.
Quote:
And what about ByRef parameters?
Tokens are a union of int64, double, string, and (pointer to) variable. ByRef parameters use said pointer to variable.


ahklerner, when did you add "Not a real bug" to your signature? Laughing (nice to know, btw)
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 3959
Location: Pittsburgh

PostPosted: Sat Sep 29, 2007 3:36 am    Post subject: Reply with quote

lexikos wrote:
ahklerner, when did you add "Not a real bug" to your signature? Laughing (nice to know, btw)
I tried to kill it with my thumb a couple of times…
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 1281

PostPosted: Sat Sep 29, 2007 4:01 am    Post subject: Reply with quote

lexikos wrote:
any assignment operation (except post++) results in a reference to the variable itself.

This is the one which surprised me, not the operation of it.

Quote:
The called function doesn't use the stack - it uses (quite literally) variables which represent the parameters.

Really? Are the variables of parameters assigned before the function call?
I mean, created, which is not a quite correct term with AHK, but I think you know what I mean.

Quote:
Also, the stack is an array of (pointers to) tokens, so it doesn't matter how large each value is.

Now you said it. You should've said it from the start. Here is a quote from myself:
Quote:
the stack should be filled with the parameters, pointers to the strings of actual parameters in AHK case, in reverse order,

Quote:
ByRef parameters use said pointer to variable.

That was the point. If it's possible with ByRef parameters, why not use it, the pointer to (the variable of) the evaluated value, with other parameters? It's simple and elegant.

BTW, all my statements are done on the assumption that the parameters are created inside the function, not before the function call.
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2494
Location: Australia, Qld

PostPosted: Sat Sep 29, 2007 5:56 am    Post subject: Reply with quote

Quote:
BTW, all my statements are done on the assumption that the parameters are created inside the function, not before the function call.

I believe the parameter variables are created only once: when the function is defined (Script::DefineFunc().)
Quote:
Really? Are the variables of parameters assigned before the function call?
I mean, created, which is not a quite correct term with AHK, but I think you know what I mean.

They are assigned values (func.mParam[j].var->Assign(token);) directly before the function is called (aResult = func.Call(result);). If the function has any instances already running, its variables are backed up beforehand (Var::BackupFunctionVars()) and reused.

Quote:
If it's possible with ByRef parameters, why not use it, the pointer to (the variable of) the evaluated value, with other parameters? It's simple and elegant.
I'm not sure what you mean by that. The "evaluated value" doesn't have/isn't a variable unless the operation was an assignment (or there was no operation, as in func(var).) In these cases, the variable is pushed onto the stack regardless of whether it will be passed to a ByRef parameter (since - I guess - this wouldn't be known.) If the parameter isn't ByRef, the (result) variable's contents are assigned to the parameter.


There are probably inaccuracies in what I've posted, but I try not to state anything matter-of-factly until I'm confident it is fact. Line::ExpandExpression() is some ~2600 lines long, so I'm far from fully understanding it. Rolling Eyes

Btw, when I say "stack", I'm referring to a local variable of Line::ExpandExpression(), not the system's call stack.
Quote:
Now you said it. You should've said it from the start.
I'm still learning (rereading the code) as I post. Smile
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 1281

PostPosted: Sat Sep 29, 2007 8:33 am    Post subject: Reply with quote

lexikos wrote:
I believe the parameter variables are created only once: when the function is defined (Script::DefineFunc().)

Yes, so I said:
Quote:
I mean, created, which is not a quite correct term with AHK, but I think you know what I mean.

Quote:
They are assigned values (func.mParam[j].var->Assign(token);) directly before the function is called (aResult = func.Call(result);). If the function has any instances already running, its variables are backed up beforehand (Var::BackupFunctionVars()) and reused.

It's a surprise for me, nevertheless, seems feasible.

Quote:
Quote:
If it's possible with ByRef parameters, why not use it, the pointer to (the variable of) the evaluated value, with other parameters? It's simple and elegant.
I'm not sure what you mean by that.

What I thought was the following:
1) pre-evaluating the expressions of parameters. Then, finally allocate and fill the buffers for the parameters in reverse-order. Dereferencing of the variables for ByVal parameters are done at this stage.
2) After each step of allocating and filling the buffer, push the pointer of the buffer onto the stack. If the parameter is of ByRef, then use the pointer of the variable. After finishing them, call the function. Then, the function pops the pointers off the stack and assigns the parameter-variables to correspond to the pointers. At this assigning level, no need for the function to differentiate between ByRef and ByVal. They are on an equal footing.

According what you said, however, AHK skips the second step, directly assigns the parameter-variables in the first step.

Quote:
Line::ExpandExpression() is some ~2600 lines long,

Wow, good luck then. Wink
Back to top
View user's profile Send private message
Xander (not logged in)
Guest





PostPosted: Sat Sep 29, 2007 4:25 pm    Post subject: Reply with quote

ahklerner wrote:
Xander wrote:

....To illustrate a problem with using temporary variables consider:
Code:
ATimerLabel:
   Loop, 100 {
      temp := 100 - A_Index
      func(temp)
   }
   return
F1::
   temp := func2()
   Send, %temp%
   return


That code won't work on my machine Crying or Very sad

Sorry, it wasn't meant to be a functional script!

You would have to give definitions for func(param) and func2() as well as use SetTimer to have ATimerLabel run every so often.
Back to top
Xander



Joined: 23 Sep 2007
Posts: 142

PostPosted: Sat Sep 29, 2007 5:00 pm    Post subject: Reply with quote

Sean wrote:
lexikos wrote:
any assignment operation (except post++) results in a reference to the variable itself.

This is the one which surprised me, not the operation of it.

post-fix ++ by itself is not considered an assignment because their is no logical variable to assign it to (except perhaps an imaginary one).

That is why this is not allowed:
Code:
index = 1
MsgBox(index, index++)

MsgBox(a, ByRef b) {
   MsgBox, % a " " b
}


But this is:
Code:
index = 1
MsgBox(index, ++index)

MsgBox(a, ByRef b) {
   MsgBox, % a " " b
}
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 1281

PostPosted: Sun Sep 30, 2007 12:23 am    Post subject: Reply with quote

Xander wrote:
post-fix ++ by itself is not considered an assignment

Where is it stated in the documentation? If it's not in the documentation, this statement is mere tautological transformation, inaccurate IMO as it ignores the incrementation of the variable itself, of my statement. I had immediately tested the following after my first post on this:
Code:
i:=0
Test(++i)
Test(i++)

Test(ByRef i){
   MsgBox, %i%
}


Quote:
because their is no logical variable to assign it to (except perhaps an imaginary one).

I don't know what you're talking about. Please clarify on this: an assignment is occurred or not?
Back to top
View user's profile Send private message
Xander



Joined: 23 Sep 2007
Posts: 142

PostPosted: Sun Sep 30, 2007 3:20 am    Post subject: Reply with quote

Sean wrote:
Where is it stated in the documentation? If it's not in the documentation, this statement is mere tautological transformation, inaccurate IMO as it ignores the incrementation of the variable itself, of my statement.

I am not sure what you are looking for here. Anyway the documentation has this to say about ++
Quote:
If it appears before the name, the operation is performed immediately and its result is used by the next operation. For example, Var := ++X increments X immediately and then assigns its value to Var. Conversely, if the operator appears after the variable's name, the operation is performed after the variable is used by the next operation. For example, Var := X++ increments X only after assigning the current value of X to Var.

Which of course is the normal behaviour of ++ in C++ and Java (and probably many other languages as well).

Sean wrote:
I don't know what you're talking about. Please clarify on this: an assignment is occurred or not?

Yes an assignment occurs, but the variable x is not returned, the value of x is returned.

As you probably are aware, x++ becomes:

return x
x += 1

But if there is nothing to return x to, for example if you just have this as a statement:

x++

the return portion is essential thrown out and it mearly becomes

x += 1

Similarly ++x becomes:

x += 1
return x

And if you just have ++x on a single line, it is no different from x++ as there is nothing to return x to after it is incremented.

So when you have ++x as an expression in a parameter list, the variable x is incremented and then returned, whereas if you have x++ as an expression in a parameter list, the value of x is return and then x is incremented.

I am not sure I follow why you think the variable (or address of) x should be returned when x++ is used, can you please explain? I think I've explained my reasoning about as best as I can unless I know more about what you are thinking.



Also, allow me to clarify that I think the value should be returned regardless of whether ++x or x++ or x += something is used. Though I see why it isn't in the current 2 stage implementation because it is not known if the parameter will be ByRef or not. This probably could be allieviated by using a single step, that is, do the evaluation and determine if the parameter is ByRef before continueing to the next parameter evaluation.
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 1281

PostPosted: Sun Sep 30, 2007 9:35 am    Post subject: Reply with quote

Xander wrote:
And if you just have ++x on a single line, it is no different from x++ as there is nothing to return x to after it is incremented.

I'm impressed. You really explained thoroughly.
BTW, this is what made me think x++ would behave differently when it's the sole operation.
After the post, I felt some deja vu. I think I already tested it once, but completely forgot about it.
So, I seem to have a difficulty to accept this behavior.

Quote:
Also, allow me to clarify that I think the value should be returned regardless of whether ++x or x++ or x += something is used.

Personally I agree with you on this, but it may be not so simple to change.
I'm regarding it as the cost I have to pay for to enjoy the great flexibility of AHK.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Bug Reports All times are GMT
Goto page Previous  1, 2, 3, 4, 5  Next
Page 3 of 5

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group