Jump to content

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

Passing variables w/assignments to ByRef function parameters


  • Please log in to reply
23 replies to this topic
polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
Take the example:
; x := y := false ; no difference 
MsgBox % &(y := true) ; works
MsgBox % f([color=blue]([/color]x := true[color=blue])[/color]) ; error: 'must pass variable'

f(ByRef v) {
	Return &v
}
Is there a reason why the latter does not work?

autohotkey.com/net Site Manager

 

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


PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
You pass a value (true, the result of the assignment) where the ByRef parameter expect a variable where to write back something. It can't work, that's not a bug.
Well, I would report the first MsgBox as bug, though... :-)
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
Yes, that's supposed to be possible but the syntax checker wrongly sees it as an error. It will be fixed in the next update.

Thanks.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
A (slow and ugly) workaround is here.

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005

Yes, that's supposed to be possible but the syntax checker wrongly sees it as an error. It will be fixed in the next update.

:?: I suppose you pass the leftmost variable of the expression. That's logical and illogical at the same time. You pass a value, but make an exception and extract some semantic value from the expression to know which variable to use. I doubt it would work in most languages, but why not, if that make Titan happy with not much complexity in the language engine.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

I suppose you pass the leftmost variable of the expression. That's logical and illogical at the same time.

That's what Laszlo and I thought, but it's by design (which is why the function should work).

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

... pass the leftmost variable of the expression. That's logical and illogical at the same time.

It could go both ways... The assignment x := y has a value, which can be the right-value y, or the left value x, after the assignment. Both allow chaining assignments, but the left value provides more flexibility, possibilities. The most obvious is the example of initializing ByRef parameters at call: f(a:=1,b:=2). It is more compact (without being ambiguous or obfuscated) than a:=1,b:=2 ... f(a,B). On could also replace "Very_long_variable_name := 1 - Very_long_variable_name" with the shorter, less error prone (Very_long_variable_name -= 1) *= -1, and countless other examples. You can remember the rule, which is simple and intuitive.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
The above does not mean I like, what is in the documentation: "The precedence of the assignment operators is automatically raised when it would avoid a syntax error or provide more intuitive behavior... ++Var := X is evaluated as ++(Var := X)".

For me the intuitive behavior was (++Var) := X, which makes sense if X is an expression containing Var. As with other operators, the left parameter (++Var) is evaluated first, than the expression X, finally the assignment takes place. It assumes, of course, that ++Var is the same as Var += 1, which is an assignment, returning the left-value Var.

Var++ := X is less intuitive. Var++ returns the left value Var, *before* it gets incremented. But when will it be incremented? After the assignment or before evaluating X?

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
I see what you mean. But to make the behavior more selective would significantly reduce performance since it occurs in a critical section of code. I think the current behavior does more good than harm because examples like the ones you cited seem much rarer than ones where this behavior helps things.

Also, the fact that ++Var produces a left-value isn't even documented due to obscurity. This obscurity is increased by the rarity of someone deliberately wanting to do ++Var := Expression-Involving-Var, which makes the total probability extremely low -- especially since most such expressions can be written more intuitively with the ++ inside the right side of the expression rather than the left.

If you still think the behavior should be changed, it would help to see some more common examples.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
I cannot give "common examples" to what I think represents bad programming style.

assignments are evaluated from right to left

It could mean that first the right hand side is evaluated then the left:
x:=1, (++x) := 1-x :
1-x → 0; ++x: x ← 1; ":=" : x ← 0
In this case ++ has no effect.

The current behavior is that the left hand side is evaluated first (still the chain of assignment can be evaluated from right to left)
++x: x ← 2; 1-x → -1; ":=" : x ← -1
It makes sense.

Therefore, an extended explanation would be helpful, like
"a series of assignments are evaluated from right to left, but in each one the left hand side is evaluated first, than the right hand side".

The precedence of the assignment operators is automatically raised when it would avoid a syntax error or provide more intuitive behavior. For example: not x:=y is evaluated as not (x:=y). Similarly, ++Var := X is evaluated as ++(Var := X); and Z>0 ? X:=2 : Y:=2 is evaluated as Z>0 ? (X:=2) : (Y:=2)

I sound like a broken record, but these examples are counterintuitive for me. One could think that
1: "not x:= y" means "(x := not x) := y"
2: "++Var := X" means (++Var) := X
The third example can be handled with the rule that the precedence of "?" is what it is now, but the precedence of ":" is lower than that of the assignments. It means, assignments are executed after "?". (But no commas between "?" and ":", or after ":", like c ? x:=a, y:=b : z:=d.)

In any case, I would vote for getting rid of the raising the precedence of some assignments, because they promote bad programming style, reduce portability and legibility, and cause confusion.

If an assignment is used as the input for some other operator, its value is the variable itself.

This sounds sufficient to explain that it is a left value, so ByRef parameters can be initialized in the function call, which is very common.

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004

an extended explanation would be helpful, like
"a series of assignments are evaluated from right to left, but in each one the left hand side is evaluated first, than the right hand side".

In my opinion it's too obscure to document. How many people other than you and Titan use an expression on the left side of the assignment operator (other than a simple chain like x:=y:=expression)? I can't imagine that more than 1% of users would even consider doing it.

I have this attitude because the documentation has been criticized as being too long, which I agree with. I have a large list of things deliberately omitted because I feel that including them would add more confusion than they're worth. The documentation would probably be 30% longer if they were all included.

1: "not x:= y" means "(x := not x) := y"
2: "++Var := X" means (++Var) := X

Other than side-effects flowing from the left side to "a right side that involves x" (which isn't even documented so perhaps shouldn't be relied on), the examples above serve no purpose. I can't see more than 1 in 1000 users deliberately writing them.

The third example can be handled with the rule that the precedence of "?" is what it is now, but the precedence of ":" is lower than that of the assignments. It means, assignments are executed after "?". (But no commas between "?" and ":", or after ":", like c ? x:=a, y:=b : z:=d.)

I had a hard time getting ternary to work, so I'd be reluctant to revisit this area.

I would vote for getting rid of the raising the precedence of some assignments, because they promote bad programming style

It doesn't seem bad to me, so we should probably just stay divided on that point.

reduce portability and legibility

It's up to the writer of the script what's legible. Personally, I find "if not x:=y" to be a possible convenience since I write it often in C++. Others can add parentheses if it seems more readable to them.

and cause confusion.

In my opinion, the harmful cases don't occur often enough to cause confusion in more than 1% of users. And I think at least 5% are helped by this behavior, which is why I chose it. PHP and C++ implement a subset of this behavior: neither of them has a strict operator precedence table. Instead, they use a rule-based approach because it enhances convenience and intuitiveness. You argue that they do the opposite, which they do, but only in cases that are much rarer.

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

In my opinion it's too obscure to document. How many people other than you and Titan use an expression on the left side of the assignment operator (other than a simple chain like x:=y:=expression)?

I don't. The point is how many of us would understand a script posted, which use these tricks? We could just ignore them because of their bad style, but if they do something useful fast, it would be very hard to decipher and adapt. And there will be many related ask-for-help posts.

These kinds of code can be written accidentally (by adding something to a working script). We have "x := 1-x+x*x", and want to increment x beforehand. Instead of the correct "++x, x := 1-x+x*x", one might accidentally write "++x := 1-x+x*x". It is hard to notice that it does not do, what was intended.

I think it is important to know how operators are evaluated. Is the left operand *always* evaluated before the right one? We have already seen a number of scripts posted with "++x +2*x" like constructs. Dependent on if 2*x is computed first, or ++x we have different results. Maybe it is documented somewhere, but I missed it. Without it the ++ constructs make no sense. Furthermore, we know that there is "Short-circuit Boolean Evaluation". It means, that ++ is very dangerous in Boolean expressions, like: "x<1 and ++y>2". I'd like a reference in the help at ++ and --, to the short-circuits, or telling there that if they appear in Boolean expressions (like function calls), they prevent short-circuit evaluations.

How about an arithmetic short circuit? If x=0, the evaluation of "x*z*y++" can be short-circuited, and y++ never executed. It would be nice to know that this never happens.

the documentation has been criticized as being too long, which I agree with. I have a large list of things deliberately omitted because I feel that including them would add more confusion than they're worth.

I don't think the length of the documentation is a problem. You could always add a link to advanced topics, most users don't need to look up, and so the core of the help can remain short.

Other than side-effects flowing from the left side to "a right side that involves x" (which isn't even documented so perhaps shouldn't be relied on), the examples above serve no purpose. I can't see more than 1 in 1000 users deliberately writing them.

Again, the problem is not supporting the 0.1% of the users, who write such code, but the rest of us, who try to understand what happens in these rare scripts, and try to answer ask-for-help questions about these.

I think these "side-effects flowing from the left side to a right side that involves x" are implicitly documented. ":=" is defined as an operator, like + or /, and if side effects can be used with other operations, it is natural to assume they work also with ":=", listed in the same table.

I would vote for getting rid of the raising the precedence of some assignments, because they promote bad programming style

It doesn't seem bad to me, so we should probably just stay divided on that point.

I meant the "++Var := X" is bad style, because it relies on an exception, breaking the precedence rules (++ is of higher precedence than ":="). It is hard to remember that a perfectly valid operation is not evaluated according to the standard rules, but it forms an exception.

I find "if not x:=y" to be a possible convenience since I write it often in C++. Others can add parentheses if it seems more readable to them.

Yet again, the issue is not with writing obfuscated code for one's own use, but for the rest of the world understanding and being able to modify. Even a few months later the original author might not understand his own code if it was not built based on simple, logical rules.

the harmful cases don't occur often enough to cause confusion in more than 1% of users. And I think at least 5% are helped by this behavior, which is why I chose it. PHP and C++ implement a subset of this behavior: neither of them has a strict operator precedence table. Instead, they use a rule-based approach because it enhances convenience and intuitiveness.

I don't know PHP, but many of us consider C++ the worst programming language ever invented. If you don't use C++ for a while, you have to constantly look up the rules. It is much better with "cleanly" designed languages. Especially, I don't think a typical AHK user write scripts that often, so simpler rules are even more important. When I write my scripts, I have to try out many pieces in isolation, because I don't always remember the right syntax. And if I have to debug scripts others wrote, it is even worse.

But I may not be a typical user… Also, when I write I'd get rid of something or I'd do it another way, it means: please consider it the next time you have to change those parts.

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004

The point is how many of us would understand a script posted, which use these tricks? We could just ignore them because of their bad style, but if they do something useful fast, it would be very hard to decipher and adapt. And there will be many related ask-for-help posts.

Scripts that rely on such obscure behavior, without using clarifying parentheses, are really performing a mild form of obfuscation. I don't think language-design decisions should give much weight to them.

I think it is important to know how operators are evaluated. Is the left operand *always* evaluated before the right one? We have already seen a number of scripts posted with "++x +2*x" like constructs.

Since it's not documented, any script that relies on it is at risk of breaking with future releases. Due to obscurity, documenting it might add too little value, even if it's best for the language to guaranty left-to-right evaluation (which I don't have the experience to say).

I'd like a reference in the help at ++ and --, to the short-circuits, or telling there that if they appear in Boolean expressions (like function calls), they prevent short-circuit evaluations.

If you mean that ++/-- won't take effect when short-circuited, I think it's covered well enough in the short-circuit section. But I suspect you mean something else that I've missed.

How about an arithmetic short circuit? If x=0, the evaluation of "x*z*y++" can be short-circuited, and y++ never executed. It would be nice to know that this never happens.

That's a good point but the documentation is not intended to be a complete technical specification for the language. It's intended to be as useful as possible, which in turn limits how long it can be.

I don't think the length of the documentation is a problem. You could always add a link to advanced topics, most users don't need to look up, and so the core of the help can remain short.

That might happen someday, but I still feel it can do more harm than good, in part due to maintainability: the material is already long enough that it's becoming difficult to keep accurate with each new version.

":=" is defined as an operator, like + or /, and if side effects can be used with other operations, it is natural to assume they work also with ":=", listed in the same table.

That's a good point. However, due to rarity of all this, I think it matters little in the grand scheme of things. I chose a behavior that is admittedly too broad to be optimal, but performs better than something more specific. Although it seems like the best compromise, I've been known to emphasize performance too much.

I meant the "++Var := X" is bad style, because it relies on an exception, breaking the precedence rules (++ is of higher precedence than ":="). It is hard to remember that a perfectly valid operation is not evaluated according to the standard rules, but it forms an exception.

Perfectly valid but extremely rare. Its weighting is too little to offset the benefits of the entire package (and I saw it as a package deal for the performance reason above).

Even a few months later the original author might not understand his own code if it was not built based on simple, logical rules.

In practice, I think most people do not memorize or consult the precedence tables, and so wind up using parentheses. In cases where this isn't so, I believe the current behavior does more good than harm within the constraint that it's an all-or-none package.

when I write I'd get rid of something or I'd do it another way, it means: please consider it the next time you have to change those parts.

I generally won't make script-breaking changes unless there's a clear benefit. Since I believe the current behavior is the best compromise between performance and convenience, it will probably stay this way unless a superior benefit/cost can be demonstrated for some other approach. So far, your arguments don't seem oriented to real-world cost/benefit; instead, they seem to over-emphasize purity and consistency. While I value those virtues, I won't entirely sacrifice ergonomics and convenience for them.

jonny
  • Members
  • 2951 posts
  • Last active: Feb 24 2008 04:22 AM
  • Joined: 13 Nov 2004

I don't. The point is how many of us would understand a script posted, which use these tricks? We could just ignore them because of their bad style, but if they do something useful fast, it would be very hard to decipher and adapt. And there will be many related ask-for-help posts.

...

Again, the problem is not supporting the 0.1% of the users, who write such code, but the rest of us, who try to understand what happens in these rare scripts, and try to answer ask-for-help questions about these.

...

Yet again, the issue is not with writing obfuscated code for one's own use, but for the rest of the world understanding and being able to modify. Even a few months later the original author might not understand his own code if it was not built based on simple, logical rules.


I've seen this argument before, and I think it's time I said something. Why should we account for [sometimes deliberately] obfuscated and unclear code? The language wasn't designed to be used that way, and so... it shouldn't be used that way! That would be like expecting a car company to make their seats easier to remove, just in case someone wanted to improve the car's power-to-weight ratio for better performance. After all, they sell cars to people who plan to modify them for street races, so they should devote some effort to expanding that market, right?

Not a direct correlation, of course, but my point is that this kind of thing doesn't need to be considered. So what if someone decides to post some horrible code? That's their problem. If Dennis Ritchie had accounted for badly written code, C might not be as flexible and powerful. I know this is actually a point of controversy, but C takes flexibility to the extreme. In AutoHotkey this is an even smaller concern. And speaking of C, I want to address something else as well:

I don't know PHP, but many of us consider C++ the worst programming language ever invented. If you don't use C++ for a while, you have to constantly look up the rules. It is much better with "cleanly" designed languages.


So, who's "many of us?" Because I think many more believe otherwise (see links below). C++ is second only to Java and its predecessor in overall popularity, and if you include Java and C (as variants on a theme), everything else is squiggling on the bottom of the charts (is your criticism specific to C++?). And that might be due largely in part to businesses, but I use it, and a lot of my friends do too. Those that don't still don't consider it a bad language; at worst, they denounce it in favor of their language of choice.

As for "clean," I think the real question is whether the language is designed for the computer or the human. Thus this discussion is closely linked to the balance between intuitive programming and pure (machine-wise) programming.

And by the way, what language would you advocate in place of C++ (and company)?

TIOBE Programming Community Index
Usenet Statistics

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

I think it is important to know how operators are evaluated. Is the left operand *always* evaluated before the right one? We have already seen a number of scripts posted with "++x +2*x" like constructs.

Since it's not documented, any script that relies on it is at risk of breaking with future releases

This is a very important message. I think it should appear in several places in the help. All of the following and similar constructs are "at risk of breaking with future releases"
*p++ + *p*256
++x>1 ? x : y
++x + 2*x
f(x:=1,x)
There are many similar examples already posted. If I see something similar, I will post a pointer to the relevant place in the Help.