Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Unexpected Result (left to right paramater passing)


  • Please log in to reply
61 replies to this topic
polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
I may have read defensive but that wasn't my intent, sorry.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

proclaim that:

i, ++i, j-- * k

is bad style!

Especially since it is in the documentation that comma separated list of expressions are evalutated from left to right…
AHK would unfortunately be a language that violates the Principle of Least Astonishment, hence the function name SurpriseMe.

function parameters aren't evaluated as multi-statements when they should be.

They are very different things. Function parameters are syntactically different, they are converted to implicit assignments, they have default values, etc. If you want function parameters to be evaluated from left-to-right, post it in the Whish list.

As I said repeatedly, the documentation does not specify the evaluation order, and having a different order than what you like, is not a bug.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
It shouldn't be like that anyway.
This shold be fixed.
Posted Image

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

It shouldn't be like that anyway..

If you mean, the parameter evaluation order should be specified in the AHK documentation, I agree. The evaluation order of sub-expressions could also be specified, otherwise the result of expressions like i+i++ is undefined, as already discussed a couple of times. Post it to the Wish list! There is no bug here.

Xander
  • Members
  • 140 posts
  • Last active: Nov 13 2007 08:31 PM
  • Joined: 23 Sep 2007

function parameters aren't evaluated as multi-statements when they should be.

They are very different things. Function parameters are syntactically different, they are converted to implicit assignments, they have default values, etc.

Syntactically different? I don't see why you think an assignment makes such a big deal. In the example given in the documentation of "list of expressions", three of the four so-called expressions are assignments! Perhaps one or both of y+=2 and ++index are not caught by the untrained eye as assignments but they are.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
I don't really care how do you call it. Bug or not, this system is bug prone.

The problem is apperent - if other languages do it one way and AHK another, that is not good, especialy if we take into account C orientation in expressions.
Posted Image

Xander
  • Members
  • 140 posts
  • Last active: Nov 13 2007 08:31 PM
  • Joined: 23 Sep 2007

It doesn't prove anything, just a guess.
It may be done this way to ease the preparation of the stack.
Notice the addresses of parameters.

Test(1,2,3,4)

Test(prm1, prm2, prm3, prm4){
	MsgBox, % &prm1 . "|" . &prm2 . "|" . &prm3 . "|" . &prm4
}

probably just means the 4 parameter addresses are grabbed as a group. Says nothing about which order they are assigned their values.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

probably just means the 4 parameter addresses are grabbed as a group. Says nothing about which order they are assigned their values.

I didn't claim that the assignments are really done in reverse order, which doesn't explain your second example, then.

Anyway, try this.

test := 0
MsgBox, % &test . "`n"
	. Test(1,1,1,1)
	. Test(1234,1234,1234,1234)
	. Test(12345678,12345678,12345678,12345678)

Test(prm1, prm2, prm3, prm4){
	Return	&prm4 . "|" . &prm3 . "|" . &prm2 . "|" . &prm1 . "`n"
}

To me, it seems two-stage operations. (again, a mere guess)
1) pre-evaluation (in left-to-right order)
2) final assignment (in right-to-left, i.e., reverse, order)

But, in the first pre-evaluation stage, AHK doesn't fully evaluate all the expressions, maybe for performance or for ByRef parameters. I mean, if the parameter is a variable, AHK doesn't dereference it at this stage, just keep the reference to the variable.

Another example:

Test(test.=1234,test.=5678)

Test(prm1, prm2){
	MsgBox, % prm1 . "|" . prm2 . "`n"
		. &prm1 . "|" . &prm2
}


Xander
  • Members
  • 140 posts
  • Last active: Nov 13 2007 08:31 PM
  • Joined: 23 Sep 2007

Anyway, try this.

test := 0
MsgBox, % &test . "`n"
	. Test(1,1,1,1)
	. Test(1234,1234,1234,1234)
	. Test(12345678,12345678,12345678,12345678)

Test(prm1, prm2, prm3, prm4){
	Return	&prm4 . "|" . &prm3 . "|" . &prm2 . "|" . &prm1 . "`n"
}

To me, it seems two-stage operations. (again, a mere guess)
1) pre-evaluation (in left-to-right order)
2) final assignment (in right-to-left, i.e., reverse, order)


I don't understand how you are getting evaluation/assignment order based on memory addresses.

All needed memory addresses needed in a statement should be grabbed before any evaluations in the statement. Their order probably has more to do with how available memory addresses are retrieved.

But, in the first pre-evaluation stage, AHK doesn't fully evaluate all the expressions, maybe for performance or for ByRef parameters. I mean, if the parameter is a variable, AHK doesn't dereference it at this stage, just keep the reference to the variable.

I think you are close here. Did you see my example in a prior post? If there is evaluation involved, those are done first. Then anything that is just being passed as the value of a variable is assigned. I cannot think of any good reason to do it this way. It certainly can't be faster or anything, all the same operations must be done regardless. In fact it probably introduces a slowdown somewhere as it is necessary to check if an evaluation is required or not and reorder things.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

I don't understand how you are getting evaluation/assignment order based on memory addresses.

Let's see if others think the same as you on the result of my example.
Anyway, it's not a real proof as I said, however, a highly likely evidence IMO.

If there is evaluation involved, those are done first.

Looks like you're using the term evaluation slightly differently from me here.
My usage of it also includes dereferencing the variables too in this case.

I cannot think of any good reason to do it this way.

Assuming my guess about the two-stage operation is correct, IMO, the final assignment stage may be termed more accurately as the stack preparation.

Xander
  • Members
  • 140 posts
  • Last active: Nov 13 2007 08:31 PM
  • Joined: 23 Sep 2007

Assuming my guess about the two-stage operation is correct, IMO, the final assignment stage may be termed more accurately as the stack preparation.

Is there a good reason to do a two stage process? If I am understanding you correctly anything that requires an operation is done,
then the variables are dereferenced.

Take for example:

index = 1
doSomething(index, ++index, index/2)

becomes

[operation stage]
a = ++index
b = index/2

[dereference stage]
p1 = index
p2 = a
p3 = b
doSomething(p1, p2, p3)

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

Is there a good reason to do a two stage process?

I don't really know. I don't even know if it's really done in two stage.
Don't get me wrong. What I have told so far were about what I observed and guessed, not about my opinion how things should be done.
I can guess further why it's been done this way, but I think better wait for the reply from Chris.
One thing though. Some operation in the reverse-order is inevitable, as the stack should be filled with the parameters, pointers to the strings of actual parameters in AHK case, in reverse order, unless AHK implements its own operation to reorder.

If I am understanding you correctly anything that requires an operation is done, then the variables are dereferenced.

Again, I didn't say it should be done this way. My opinion on this is that the dereference of the variables should be done too in the first stage, not in the second stage. However, I understand it could really slow down the operations if the variable holds very large string, and simply be a wrong operation if the corresponding parameter is of ByRef.

I think it's a consequence that AHK really treats the parameters as mere comma separated expressions (in the first stage).

test.=1234, test.=5678
In this mere comma separated expression, dereferencing the variable test after assignment is really meaningless.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

I don't understand how you are getting evaluation/assignment order based on memory addresses.

Perhaps a more straight-forward test would be:
f(p(1),p(2),p(3),p(4))

f(a,b,c,d) {
  msgbox f(%a%,%b%,%c%,%d%)
}
p(n) {
  msgbox param %n%
  return n
}
The message boxes show the order: 1, 2, 3, 4.

Is there a good reason to do a two stage process?

I don't really know. I don't even know if it's really done in two stage.

It is, in the sense that value -> parameter assignment is done after (and is distinctly separate from) expression evaluation. I've spent several hours going over Line::ExpandExpression(), which does all the work. (I still don't understand it fully...)

But, in the first pre-evaluation stage, AHK doesn't fully evaluate all the expressions, maybe for performance or for ByRef parameters.

I think it would be more accurate to say that:[*:5d64umzz]AHK fully evaluates each expression, pushing each result onto the stack,
[*:5d64umzz]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.)

However, I understand it could ... simply be a wrong operation if the corresponding parameter is of ByRef.

As (it seems) you've concluded, ByRef parameters rely on the aforementioned behaviour.


By the way, 'return' behaves in a similar way when using multi-expressions:
msgbox % f(1)
f(i) {
  return i, ++i  ; returns 2
  ;return i+0, ++i  ; returns 1
}


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
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.

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.

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. Like an inline function definition. These parameters should NOT be evaluated at entering the function (call), so a fixed evaluation order precludes this useful feature forever. Like sum(i:=2,i<=10,i+=2,i**2) could compute the sum of the first 5 even squares.

- Lazy evaluation could prevent run-time errors. Like this:
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.

- Lazy evaluation could save a lot of memory and execution time, not evaluating complex expressions in parameters, which are not actually used.

A fix parameter evaluation order would necessitate complex if-then-else statements or ternary operators, outside of the function calls.

AHK leaves open the possibility to implement these features, by not specifying the parameter evaluation order. It is important for a fast evolving language, and relying on undocumented features could break your code at the next AHK release, without you knowing the cause. (Changes of documented features get documented, so you would know what hit you.)

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
...Nevertheless, you can experiment also with a version of lexikos' script, to figure out the current AHK behavior (which you must not rely on):
f(p(1),p(2),p(3))

Msgbox %order% ; 1 2 3

order =

g(a:=p(1),b:=p(2),c:=p(3))

Msgbox %order% ; 1 2 3

order =

i = 0

f(p(i++),p(++i),p(i))

Msgbox %order% ; 0 2 2



f(a,b,c) {

}

g(a,ByRef b,c) {

}

p(n) {

  Global order

  order .= n . "  "

}