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
Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

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.

Not only that, but AutoHotkey actually translates both to 'ACT_ADD', which I guess is more efficient than 'ACT_EXPRESSION'.
i = 0

i++           ; i += 1  ; ACT_ADD

++i           ; i += 1

i += 1        ; i += 1

EnvAdd, i, 1  ; i += 1

i := i+1      ; i := i+1  ; ACT_EXPRESSION

listlines     ; shows the above lines

pause


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
I still don't understand, why do you guys spend so much time and effort to figure out the current, undocumented AHK behavior. It could change in any new version, without notice, since it is undocumented. Therefore, even if you know exactly what happens, your scripts must not rely on that. Is this just an intellectual exercise, a challenge? And it is the wrong section of the Forum, because there is no bug here.

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

And it is the wrong section of the Forum, because there is no bug here.

Regardless of whether you think its an actual bug or not, it is a poor design. This is because when coupled with the fact that their is no way to localize variables to a block (only to a function) in AHK, you are forced to pollute the variable namespace with temporary variables that may be accidentally reused in unappropriate places. As I outlined earlier, that can lead to nasty bugs.

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

Not only that, but AutoHotkey actually translates both to 'ACT_ADD', which I guess is more efficient than 'ACT_EXPRESSION'.

I don't know much about how AHK interprets its code, but somewhere along the way, the return part would have to be lost as it doesn't make any logical sense to return something to nothing.

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

it is a poor design. This is because when coupled with the fact that their is no way to localize variables to a block (only to a function) in AHK, you are forced to pollute the variable namespace with temporary variables that may be accidentally reused

Saying "poor design" just reflects that you don't know the reasons behind the design. Blocks will be implemented in AHK2, so polluting the name space is just a temporary problem.

Relying on side effects could save a temporary variable:
function(sideeffect1(x),sideffect2(x)) ; turned to
_s := sideeffect1(x), function(_s, sideffect2(x))
Big deal. But, by looking at the script, you don't always know if x has changed or not (the function could be defined in an included file). This is why side effects represent bad coding style. Even in C you almost always have to use & for the variables to be changed inside a function. ++ and -- represent very unfortunate exceptions, causing many nasty coding bugs just for saving a few keystrokes.

The cleanest syntax was, as in several programming languages (like Matlab)
[s1,x] := No_sideeffect1(x), [s2,x] := No_sideffect2(x), function(s1,s2)
But this is not AHK.

And I still don't know, what you want. Fix and document the parameter evaluation order? It will promote bad coding style; could make the interpreter slower and more complex; prevents optimizations; prevents future extensions, like lazy evaluation or re-evaluated expressions, etc. Of course, you can invent new syntax for the features you prevented, like = in front of the parameter, but it creates all kind of incompatibilities and an endless source of new bugs, and reflects a different overall semantics: the function definition, not the function call should tell, how the parameters are treated.

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

Saying "poor design" just reflects that you don't know the reasons behind the design.

Please enlighten me to the "reasons" behind the current implementation. You have my full attention and I am willing to learn..

Blocks will be implemented in AHK2, so polluting the name space is just a temporary problem.

I don't know what you mean here, blocks are already implemented. The only way to limit a variable to a block is to require explicit variable declarations. That is you can no longer just start using a variable, you must declare it in the top most block that it will be visible in. For instance, if I have a function in Java, I have to declare an interger value with "int" but the placement of the declaration matters:
int func(int p) {
	if (p == 0) {
		int k = 2;
	}
	return k;
}
The above will give a compile time error because the variable k is not visible outside of the if block it was declared in. Alternatively, this is valid:
int func(int p1) {
	int k = 0
	if (p1 == 0) {
		k = 2;
	}
	return k;
}
Is this the spirit of what you are talking about being planned for AHK? Because it seems it would significantly reduce performance for an interpreted language like AHK. Java gets around it by being both compiled and interpreted, Variable scopes in Java are checked at compile time so that there is no runtime performance hit with some type of layered scoping scheme that I imagine would have to be implemented for an interpreted language.

Relying on side effects could save a temporary variable:

function(sideeffect1(x),sideffect2(x)) ; turned to
_s := sideeffect1(x), function(_s, sideffect2(x))
Big deal.

First you try to minimize the problem by implying there is only ever a need to create one temporary variable. This is not true, you may be required to create multiples of temporary variables for each function call to multiples of different functions. Like I said before, this can be dangerous. And then you offer up this straw man:

But, by looking at the script, you don't always know if x has changed or not (the function could be defined in an included file). This is why side effects represent bad coding style.

Because actually, the scenario you pointed out would work perfectly well in AHK right now! Not saying that I would endorse side effects that happen by ByRef function calls (or even global variable changes, but that has more to do with my avoidance of globals whenever possible as a general rule). In fact all side effects would currently work in the current implementation. That is, the expressions are evaluated in left to right order, so any function call that modifies a global variable or one of its variables passed ByRef will be seen by the next expression after it and so on.

No where have i indicated I want to rely on side effects nor is that even the issue. The issue is what value is passed to the function when an expression is evaluated. One would expect it to be the value the expression returns, but it is instead the value of the variable that is returned after all expressions are evaluated (sometimes as in the case of x+1 or y-func() or z++ a new variable is created so those expressions behave as one would expect).

And I still don't know, what you want. Fix and document the parameter evaluation order?

As has been determined, the evaluation order is not the problem. The expressions are evaluated from left to right always. It is the manner in which they get their values assigned to them that seems odd.

It will promote bad coding style; could make the interpreter slower and more complex; prevents optimizations; prevents future extensions, like lazy evaluation or re-evaluated expressions, etc.

I don't think you've adequately explained any of this. As I said earlier, I don't think the implementation has any optimization advantages, it is merely the side effect of the two stage evaluation. Of course you could be right and I might be wrong, but excuse me if I don't just take your "word" for it. Are you even an authority on any of this?

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

Please enlighten me to the "reasons" behind the current implementation.

You (and I) don't know the reasons, and without them speaking about "poor design" is rude and short sighted.

blocks are already implemented

No, they are not. Only functions have separate name spaces.

The only way to limit a variable to a block is to require explicit variable declarations

You have already seen an example in AHK to the contrary: in functions undeclared variables are local (unless assume global mode is specified).

it seems it would significantly reduce performance for an interpreted language like AHK

It has not happened. Just look at functions. You can compare their speed to subroutines. Also, namespaces (blocks) are implemented in many interpreted languages, without significant performance impacts.

you try to minimize the problem by implying there is only ever a need to create one temporary variable

It was said, of course, only for the first example. You see, the second example already used two temporary variables s1 and s2.

True, they can lead to bugs, but not more likely than any other variable. With a little discipline, you can avoid problems. Like temporary variables have names starting with underscore, or temp_, or whatever you like, and they are only assumed to be valid until the next function call using them.

the scenario you pointed out would work perfectly well in AHK right now

You missed the point: relying on side effects or a specific parameter evaluation order is bad coding style, regardless of it is possible in AHK, or in any other programming language. Nobody said it would not work in AHK.

It is recommended that code not rely crucially on this specification [left-to-right parameter evaluation order]. Code is usually clearer when each expression contains at most one side effect, as its outermost operation, and when code does not depend on exactly which exception arises as a consequence of the left-to-right evaluation of expressions.

No where have i indicated I want to rely on side effects nor is that even the issue. The issue is what value is passed to the function when an expression is evaluated.

You started this thread with a bug report, complaining that SurpriseMe(index, index += A_LoopField) does not behave the way you expected. The expression in the second parameter changes the value of index, which is a side effect of the worst kind, because that variable appears in another parameter. Parameters are expressions, and if they change a variable during their evaluation, this change is a side effect. Your code should not rely on this kind of side effects.

One would expect it to be the value the expression returns, but it is instead the value of the variable that is returned after all expressions are evaluated

It is not clear, why one would expect that, because the Help (Operators in Expressions, Assign) explains "If an assignment is used as the input for some other operator, its value is the variable itself". AHK behaves as the documentation hints.

[Full specification of how function parameters are evaluated] will promote bad coding style; could make the interpreter slower and more complex; prevents optimizations; prevents future extensions, like lazy evaluation or re-evaluated expressions, etc.

I don't think you've adequately explained any of this.

I thought I did, but you seem to need more explanations.
1. It will promote bad coding style: If it is specified in details how function parameters are evaluated, scripts will be posted, which "rely crucially on this". Java and most other programming languages, tutorials recommend not to rely on this.
2. Could make the interpreter slower and more complex: Here is an example, for a slightly different issue, the order in which sub-expressions are evaluated. One possibility of evaluating expressions like sub1+sub2+sub3 is to evaluate sub3 first, pushing the result on the stack, followed by the values of sub2 and then sub3. Then pop the topmost two entries, add them, pushing the result back to the stack, and repeat. This procedure is shorter and faster than most others, but it evaluates sub-expression from right-to-left. If the order is not specified, the interpreter could use this algorithm, saving memory and processing time.
3. Prevents optimizations: If the function checks a condition and returns immediately, there can be no need to evaluate all the parameters. This optimization is not allowed if the parameters can have side effects, affecting the values of variables.
4. Prevents future extensions, like lazy evaluation or re-evaluated expressions. I already described these.

I don't think the implementation has any optimization advantages, it is merely the side effect of the two stage evaluation

The point was: a detailed specification in a fast evolving language prevents future optimization (or a change would break many legitimate scripts), not that any optimization has already been done.

Are you even an authority on any of this?

The url to my website is under all of my posts, so you can decide if you consider me an authority. But what does it have to do with the arguments? If you don't understand, just ask, someone will explain them. Even authorities can make mistakes and newbies can have good points. The arguments matter, not who makes them, don't you think?

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

... undocumented AHK behavior. It could change in any new version, without notice, since it is undocumented. Therefore, even if you know exactly what happens, your scripts must not rely on that. ... And it is the wrong section of the Forum, because there is no bug here.

I agree.

Is this just an intellectual exercise, a challenge?

For me, it is. I spend time and effort, not to figure out undocumented behaviour, but to improve my understanding of the internals of AutoHotkey, in case I ever want to contribute.

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

I spend time and effort, not to figure out undocumented behaviour, but to improve my understanding of the internals of AutoHotkey, in case I ever want to contribute.

It is a legitimate reason, but a moderator could move the thread under General Chat. Also, the investigation techniques to get behind the scenes are interesting, and could have other uses.

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

You (and I) don't know the reasons, and without them speaking about "poor design" is rude and short sighted.

I gave the main reason why I think it is a poor design in this case, we may disagree. But I never intended it to be anything but my personal opinion. This isn't grade school I hope! Criticism shouldn't be seen as an attack..

No, they are not. Only functions have separate name spaces.

I think we are defining a block completely different, I consider a block to a series of statements. A "block scope" on the other hand is what I think you are referring to as merely a block..

You have already seen an example in AHK to the contrary: in functions undeclared variables are local (unless assume global mode is specified).

That is similar but not the same. Functions have their own namespace yes and as you indicated there are two different types, assume global and assume local. Any variable that is not declared goes to the "assume" state. Let's suppose for a minute that every block used this rule.
func() {
	local one
	Loop, 5 {
		one = one . A_Index
	}
	return one
}
What would be expected of this function? Does it return 12345 or an empty? Keeping with the assume local by default for each block, it should be empty unless there was some way to import one into the next block. Maybe I am short sighted but it seems that the global/local declaration paradigm only really works with 2 scopes.

It has not happened. Just look at functions. You can compare their speed to subroutines. Also, namespaces (blocks) are implemented in many interpreted languages, without significant performance impacts.

I don't know what languages you are referring to, perhaps other script languages? I am not big on script langauges. As you may have noticed, I prefer Java above all else. Most languages seem to be what you would call "write only" that is.. trying to read something you may have written months prior is no easy task! Perl is about the worst I've experienced with all its special variables like $_ and whatnot. Anyway javascript is a language that gets many times slower every time a new slope is introduced and variables are referenced several layers outside the scope (with the global scope being the bottom layer). Javascript like AHK only has one scope to a function, but multiple scopes are introduced with "closures". That is function declaration within a function:
a = 1
f = function () {
	b = 2
	g = function () {
		c = 3
		h = function () {
			return a + b + c;
		}
	}
}
Each variable a, b, c will exist in a different scope. In the function h, the reference to variable a has to check the scope of h, then the scope of g, then the scope of f, then finally the global scope and yes it does slow things down. I had tested it long ago but it's been so long since I've written javascript.

True, they can lead to bugs, but not more likely than any other variable. With a little discipline, you can avoid problems. Like temporary variables have names starting with underscore, or temp_

The problem wasn't temporary variables overwriting non-temporary variables, it was the mistaken use of the same temporary variable. And I think it is a bit much to ask to require rereading an entire script to familiarize myself with all previous temporary variables so I don't reuse one when all I want to do is make a small edit/addition to a script months after it's inital creation :shock:

You missed the point: relying on side effects or a specific parameter evaluation order is bad coding style, regardless of it is possible in AHK, or in any other programming language. Nobody said it would not work in AHK.

I stronly disagree with the bit about relying on parameter evaluation order. While I would agree that you should not use unseen or unclear side effect dependencies (such as changing a value ByRef or a global variable), there is nothing wrong with the clearly visible effects of something as simple as:

func(index, index += something)

There is really no mystery to what it desired here. Perhaps there could be a clock cycle or two that could be saved by some random ordering of the evaluation, but really who cares? that is like .001 ms! This may have been a crucial factor back when rocks and pullies were a crucial design consideration in engineering a CPU, but not so much today :/

Heck, with all the processing I have running in the background, 99% of my cycles are still unused.

It is recommended that code not rely crucially on this specification [left-to-right parameter evaluation order]. Code is usually clearer when each expression contains at most one side effect, as its outermost operation, and when code does not depend on exactly which exception arises as a consequence of the left-to-right evaluation of expressions.

The part in bold was included in error. The portion you are quoting has to do with the evaluation order of operations within an expression, not parameter evaluation order. Here is the full context:

15.7 Evaluation Order
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.

It is recommended that code not rely crucially on this specification. Code is usually clearer when each expression contains at most one side effect, as its outermost operation, and when code does not depend on exactly which exception arises as a consequence of the left-to-right evaluation of expressions.

Which this has nothing to do with the issue at hand. It is talking about side effects within expressions (e.g x := y++ - 1/y) which can be unclear to anyone unfamiliar with order of operations.

I cannot see

i := index, j := index += something
func(i, j)

being any more clear than the simple one line example given a couple paragraphs before [stekcarb ni s'ti esuaceb sdrawkcab siht daer ot uoy tcepxe elpmaxe rof t'nod I]. Ok I did, bad example.

You started this thread with a bug report, complaining that SurpriseMe(index, index += A_LoopField) does not behave the way you expected. The expression in the second parameter changes the value of index, which is a side effect of the worst kind, because that variable appears in another parameter.

Side effects in a comma delimited list are worse than those in an expression? No.. Side effects in a list are no different than side effects from line to line. By the way, the manual DOES say that a list of expression will be evaluated left to right, that was why I was surprised.

It is not clear, why one would expect that, because the Help (Operators in Expressions, Assign) explains "If an assignment is used as the input for some other operator, its value is the variable itself". AHK behaves as the documentation hints.

Yes that is an expaneded explaination of if you have:

func(x += something)

the value of x is sent to the function, and it is a legal syntax for the language. It doesn't imply that a preceeding paramater's value may be influced by the evaluation of a parameter after it.

1. It will promote bad coding style: If it is specified in details how function parameters are evaluated, scripts will be posted, which "rely crucially on this". Java and most other programming languages, tutorials recommend not to rely on this.

But we disagree on what the Java documenation is even talking about! I've never seen anything in the Java documentation about not relying on left to right evaluation of list of expression (Which can appear in many places, not just a method call) A for loop for instance:

for (int i = ++something, int j = 100/something ; ; )

or an array initialization:

new int[] { something++, something++, something++ }

left to right evaluation is always guaranteed (I believe anyway, and it is my experience that it is the case). In fact, searching through the copy of the documentation I have on my hard drive, it has little to say about the matter other than an interpreter/compiler (Which anyone is free to make, not just Sun...) must obey those rules.

2. Could make the interpreter slower and more complex: Here is an example, for a slightly different issue, the order in which sub-expressions are evaluated. One possibility of evaluating expressions like sub1+sub2+sub3 is to evaluate sub3 first, pushing the result on the stack, followed by the values of sub2 and then sub3.

That is not a list of expressions !!! Sorry, but I am getting frustrated with the dependency of expression evaluation order that you keep bringing up. This has nothing to do with that.

Anyway, I remain skeptical of the performance improvemence of such a thing in this day and age, or if it would even be of considerable notice.

3. Prevents optimizations: If the function checks a condition and returns immediately, there can be no need to evaluate all the parameters. This optimization is not allowed if the parameters can have side effects, affecting the values of variables.

That also would affect statements after the expression! I wouldn't consider that a good idea to implement unless there was a certain syntax such as the = sign I proposed. Take:
if (func(something, i++))
	return 0
return i
would be always confusing as you don't know if i++ was evaluated or not.

The url to my website is under all of my posts, so you can decide if you consider me an authority.

Sorry, the url doesn't appear to me :( I just meant an authority on AHK technicalities. As I understand it, a person I've only heard whispers about (namely Chris) is the sole developer.

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

to improve my understanding of the internals of AutoHotkey, in case I ever want to contribute.

Just recently downloaded the source code. Was reminded of why I've loathed C++ ever since taking an intro course on it many years ago!

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

Sorry, the url doesn't appear to me :(

Posted Image ;)

func(index, index += something)

There is really no mystery to what it desired here.

Assuming left-to-right order...

By the way, the manual DOES say that a list of expression will be evaluated left to right, that was why I was surprised.

That is referring to multi-statements, which aren't the necessarily the same as parameter lists.

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

Sorry, the url doesn't appear to me :(

Posted Image ;)

Ah ha! disguised as a flashy "ignore me, probably just spam"

ahklerner
  • Members
  • 1386 posts
  • Last active: Oct 08 2014 10:29 AM
  • Joined: 26 Jun 2006

func() {
local one
Loop, 5 {
one = one . A_Index
}
return one
}

That would either have to be:
...
      one = %one%%%A_Index%
...
or
...
      one := one . A_Index
...
to work. So therefore I would expect the string "one . A_Index".....
Hmmm....Let's Try, OK:
func() {
   local one
   Loop, 5 {
      one = one . A_Index
   }
   return one
}

MsgBox, % func()

---------------------------
~~~~temp.ahk
---------------------------
one . A_Index
---------------------------
OK
---------------------------


Posted Image
ʞɔпɟ əɥʇ ʇɐɥʍ

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

That would either have to be:

...
      one = %one%%%A_Index%
...

Rule #1 of pointing out syntax mistakes: Don't make one of your own!

The above is obviously in error as there is one too many % in the middle.

to work. So therefore I would expect the string "one . A_Index".....
Hmmm....Let's Try, OK:

func() {
   local one
   Loop, 5 {
      one = one . A_Index
   }
   return one
}

MsgBox, % func()

---------------------------
~~~~temp.ahk
---------------------------
one . A_Index
---------------------------
OK
---------------------------

Is this suppose to be helpful or just trolling? This is the second such post from you.