Functions | &ByRef • ByVal

Discuss the future of the AutoHotkey language
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Functions | &ByRef • ByVal

20 Apr 2018, 19:15

I propose two ideas:
  • Add a short way to declare parameters as ByRef; recommended &param. This would save a lot of space, avoiding long horizontal lines or having to use several lines. Help when writing/reading code.
  • (Helgef's suggestion) Remove ByRef and make all parameters by default as ByRef. This would improve performance, without the need to use ByRef in all parameters (so awful). Obviously, we need a ByVal. But it does not convince me, because we would have to write ByVal, so a short way to specify it would be fine.
What do you guys think?
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: Functions | &ByRef • ByVal

20 Apr 2018, 21:56

I find very little value in ByRef parameters. I’d much rather just return an object with all the necessary data. Frankly, that’s where I see v2 heading anyways. Everything is moving to OOP
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Functions | &ByRef • ByVal

21 Apr 2018, 06:16

Hello Flipeador. My comment is a bit incomplete, default byref makes most sense for strings, numbers might just as well be passed byval, by default. In general I think passing strings byval is more controversial than passing them byref. Long horizontal lines are conveniently avoided since recent updates, Continuation by enclosure. I think your & suggestion has merit too, if there is not going to be any elaborate changes (see the hello spoiler) I (might) prefer it as is or as you suggested.
I find very little value in ByRef parameters. I’d much rather just return an object with all the necessary data
It is also about unnecessary string copies. Many functions have string parameters without altering them, then byref is always preferable.

Cheers.
hello
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Functions | &ByRef • ByVal

21 Apr 2018, 07:28

Hi Helgef,
My comment is a bit incomplete, default byref makes most sense for strings, numbers might just as well be passed byval, by default. In general I think passing strings byval is more controversial than passing them byref.
Strings would be byref, unless specified as byval, numbers would be byval, unless &/byref.
Interesting, now I see it more convincing. It does not seem like a bad idea. I think if we can add an operator as a short way of declaring ByVal, i would be more oriented to your suggestion.
I think your & suggestion has merit too, if there is not going to be any elaborate changes (see the hello spoiler) I (might) prefer it as is or as you suggested.
Yes, I believe that one of the two must be implemented. I think this last thing you're proposing is going too far, maybe for v3 ;) ?
Hello kczx3, sorry, but I do not think this has relevance on this topic.

:wave:
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Functions | &ByRef • ByVal

21 Apr 2018, 08:22

- One idea could be:
ByRef MyFunc()
to make all parameters ByRef.
- To make ByRef the default. It's a bit of a paradigm shift, it might be a good idea. If I could just stick 'ByRef' on at the front of every function (cf. 'class'), that would make it easy to fix my existing functions.
- How many functions would be broken? Do we need to be more careful when writing functions, to make sure we don't edit the input variables?
- What's more standard ByVal or ByRef? Why did AutoHotkey choose ByVal as the default?

- Btw re. using &. If you see my list of operators, there are already one or two operators with multiple uses, and this could cause confusion. See OPERATOR PRECEDENCE.
jeeswg's characters tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=26486
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Functions | &ByRef • ByVal

21 Apr 2018, 09:02

jeeswg wrote:One idea could be: ByRef MyFunc()
It does not convince me, it looks a little unnatural. What if you do not want all to be ByRef.
that would make it easy to fix my existing functions... How many functions would be broken?
I'm not sure what you mean.
Btw re. using &. If you see my list of operators, there are already one or two operators with multiple uses, and this could cause confusion. See OPERATOR PRECEDENCE.
I do not see the confusion. What do you mean?

Code: Select all

f(in_param1, &out_param2, &out_param3 := "")
{
}
The only valid operators would be := and &. :think:
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Functions | &ByRef • ByVal

21 Apr 2018, 09:46

- Do you not have functions that operate directly upon ByVal parameter variables? If parameters were ByRef by default, those functions would be broken.
- Perhaps AllByRef MyFunc() or AssumeByRef MyFunc().
- Is having a shorter way of writing 'ByRef' so useful? Please show me an example function.
- Having a way to specify all parameters as ByRef, and specifying ByVal for exceptions, that would be useful.
- I had thought of A_Field and A_Sp instead of A_LoopField and A_Space, so I do have some sympathy for your idea.
- Your argument for & being unambiguous within the function definition context is sound, however, I feel uneasy at the use of '&'. Also & would then have 3 meanings (address/ByRef/bitwise-and), 4 if you include &&.
- Crucially if ByRef became the default, ideally someone would write a script that would inform me of functions where parameter variables might be modified.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Functions | &ByRef • ByVal

21 Apr 2018, 10:05

I feel uneasy at the use of '&'. Also & would then have 3 meanings (address/ByRef/bitwise-and), 4 if you include &&.
I do not see the problem. In addition, the context in which I suggest the & is different from the others, so, as I said, there is no confusion at all. If you feel uneasy, you can always choose not to use it.
Do you not have functions that operate directly upon ByVal parameter variables? If parameters were ByRef by default, those functions would be broken.
This is version 2, I don't care. Edit: I plan to rewrite everything. Changes in how the binary data is treated (with the arrival of VarSetLength) would break the scripts too.
Is having a shorter way of writing 'ByRef' so useful? Please show me an example function
Definitely, writing &param is much better than ByRef param, I already gave the reasons above... I do not think an example is necessary. Basically, it saves us having to write 5 characters and provides more simple way when writing and reading code, all this while maintaining great clarity (and not confusion as you say, unless you use Notepad).
I had thought of A_Field and A_Sp instead of A_LoopField and A_Space, so I do have some sympathy for your idea
You're wrong, this is horrible and unclear.
Crucially if ByRef became the default, ideally someone would write a script that would inform me of functions where parameter variables might be modified.
I am not against making the ByRef parameters by default as Helgef says. I like the idea. Obviously, always provide clear information about each parameter.
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Functions | &ByRef • ByVal

21 Apr 2018, 22:27

I will not make parameters ByRef by default. Doing so would make it much harder, when looking at the code, to determine whether a variable passed to a function might be modified by that function. When a global variable is passed to a (ByRef) parameter, it is also possible for that parameter's value to unexpectedly change when the function calls some other function, or when the thread is interrupted. Parameters should act as local variables by default, allowing the function to be self-contained.

A lesser point to consider is that a function currently cannot pass its own local variable to a ByRef parameter in a recursive call to itself (whether called directly or via some other function). I do not expect this problem to be solved any time soon.

I have only rarely felt that the (sometimes non-existent) difference in performance justified the changed semantics of ByRef or the "ugliness" of declaring a parameter ByRef when the function isn't intended to modify it.

The performance cost of passing large strings to functions by value can be negated without changing the semantics of function calls - for instance, by using reference counted (or garbage collected) strings/buffers. ByRef gives the function a reference to a variable, not a reference to its value.
Flipeador wrote:In addition, the context in which I suggest the & is different from the others, so, as I said, there is no confusion at all. If you feel uneasy, you can always choose not to use it.
There is no confusion to you, because you know what it means. If a user feels uneasy, they can't choose for everyone else not to use it. (However, I can.)
I do not think an example is necessary.
I do not think it is necessary to implement your suggestion, or even to argue the reasons for not implementing it. I can simply choose not to do it.

ByRef is much clearer than &, especially considering "pass variable by reference" and "pass variable's contents by address" are similar concepts. You want &param in a function definition to mean "param accept a variable reference", but when calling it, &param passes the address of a string, number or object, not a variable reference.
You're wrong, this is horrible and unclear.
"A_Field" or "A_Sp" would only have one meaning each, and users of v1 can probably guess what that is. By contrast, & has never meant "accepts a variable by reference" in AutoHotkey before and there is little reason for anyone reading the code (who is not familiar with C++) to guess that it does so. It would have to be read about and memorized; something that's probably easier for A_Field and A_Sp. (But I'm not in favour of A_Field or A_Sp either.)


I idly considered allowing or requiring ByRef, ref or some currently-unused operator symbol in the function call instead of - or in addition to - the definition. It then becomes clear that a function or method call may modify that variable, without looking at the function or method definition (which would depend on the object). Non-ByRef variable references within the parameter list could then resolve to their value at the time the parameter is evaluated, not at the time of the function/method call, as it is now. For instance, n := 1, a.method(n, ++n) could produce the intuitive result a.method(1, n := 2) instead of a.method(2, n := 2). (This sort of thing is undefined behaviour in some languages, where order of parameters evaluation is not guaranteed.)
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Functions | &ByRef • ByVal

22 Apr 2018, 03:18

I will not make parameters ByRef by default. Doing so would make it much harder, when looking at the code, to determine whether a variable passed to a function might be modified by that function.
I do not see why it would make it much harder. As it is now, when you call a function you need to check which parameters are byref, if it was changed, you'd have to look to see which parameters are byval. Anyways, byval by default is probably more user friendly.

Cheers.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Functions | &ByRef • ByVal

22 Apr 2018, 03:57

By Val seems to be the most common case of usage. So why should we set the default to the uncommon use case?
Replacing the current string stuff with a string class and a separate binary class seems like a better idea over the long run.
Recommends AHK Studio
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Functions | &ByRef • ByVal

22 Apr 2018, 05:31

Helgef wrote:As it is now, when you call a function you need to check which parameters are byref, if it was changed, you'd have to look to see which parameters are byval.
I think you missed the point. Supposing that no functions are ByVal, what you're saying is that it's easy to tell which functions might change the parameter, because every function is ByRef and therefore every function might change the parameter.

That tells the reader nothing. Every function is suspect, and to confirm one way or the other, the reader needs to look at each assignment inside the function, and into functions that it passes its variables to, and so on. The point is that declaring ByRef tells the reader that this function intends to change the value of the caller's variable, and being ByVal by default allows the reader to easily see that the function will not affect the caller's variable because the parameter isn't ByRef. (That's putting aside global variables and sharing between nested functions.)
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Functions | &ByRef • ByVal

22 Apr 2018, 08:55

There is no confusion to you, because you know what it means.
Well... when I started with AutoHotkey it was all confusing, guess what?, I read the documentation.
Real confusion is what causes the NumXXX functions, or things like this that are still allowed: DllCall("MessageBox", Ptr,0, Str,"text", Str,"title", UInt,0).
If a user feels uneasy, they can't choose for everyone else not to use it. (However, I can.)
If that user is "all users", I think so.
I do not think it is necessary to implement your suggestion, or even to argue the reasons for not implementing it. I can simply choose not to do it.
I do not understand your position, I already gave my points in favor, only without the need for a Script as an example. :roll:

Regarding the other points, I do not share. it seems fine for me to have an optional short way to declare a parameter as byref. :wave:
Replacing the current string stuff with a string class and a separate binary class seems like a better idea over the long run.
Interesting... I would like to know more about it.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Functions | &ByRef • ByVal

22 Apr 2018, 12:50

what you're saying is that it's easy to tell which functions might change the parameter, because every function is ByRef and therefore every function might change the parameter.
Obviously all functions could change their byref parameters. But more importantly, if a function changes a parameter, and that is not the purpose of the function, it should specify that parameter to be byval. If a function isn't easily overviewed, not well documented or not from a reputable source, you will not call it anyways.
The point is that declaring ByRef tells the reader that this function intends to change the value of the caller's variable, and being ByVal by default allows the reader to easily see that the function will not affect the caller's variable because the parameter isn't ByRef.
My point is that not declaring byval is the same as declaring byref. Declaring byref do not tell the user that the function intends to change the value, it could do so solely for performance.

Consider a function library which takes a string as input to one function intended to be called by an end user, this string can not be assumed to be disposable so you declare it byval, now you have copied it once, then all help functions which you pass it around to internally can safely modify it and you do not need to declare byref in each one of them, and you avoid copying it multiple times.
nnnik wrote:By Val seems to be the most common case of usage.
I do not know that.
lexikos wrote:I can simply choose not to do it.
That is good enough :thumbup:.

Cheers.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Functions | &ByRef • ByVal

22 Apr 2018, 13:39

You seem to be misunderstanding something Helgef.
Denoting that a parameter is byref is showing that the variable is used to output data.
In some cases the variable is also used for input purposes.
In AutoHotkey passing a string byRef has the side effect of being faster than passing it byVal.
That is due to the fact that AutoHotkey copies every string when it is passed byVal even if they don't get changed by the function.
Advanced AutoHotkey users abuse this effect to further enhance the speed of their functions in specific cases.
However that usage should not affect the way this language is designed as it is an unintended effect that at most shows that we need some kind of solution for the speed problem.
That solution is not byRef.
Recommends AHK Studio
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Functions | &ByRef • ByVal

24 Apr 2018, 00:17

Flipeador wrote:I read the documentation.
Sometimes I feel you may be in the minority on that. ;)

v1 has many quirks that are well-documented, and yet still frequently cause confusion.
Real confusion is what causes the NumXXX functions, or things like this that are still allowed: DllCall("MessageBox", Ptr,0, Str,"text", Str,"title", UInt,0).
What's the problem?
If that user is "all users", I think so.
I have no idea what you mean.
I do not understand your position, I already gave my points in favor, only without the need for a Script as an example. :roll:
You may think it is useful, but just saying so won't convince anyone. Providing examples might. (It's not very likely in this particular instance.)
Helgef wrote:But more importantly, if a function changes a parameter, and that is not the purpose of the function, it should specify that parameter to be byval.
So are you now saying that the majority of function parameters should specify ByVal, including those that previously specified ByRef only for performance? :roll:
If a function isn't easily overviewed, not well documented or not from a reputable source, you will not call it anyways.
I think it's unreasonable to assume that most useful functions are well documented, or that most users will not even try to use a function they need if it isn't well documented.
Declaring byref do not tell the user that the function intends to change the value,
It should. Whether it does would depend on the user. Whether it is true would depend on the author.

Regardless, I think the number of parameters declared ByRef for the sole purpose of performance is much smaller than the number of parameters not declared ByRef.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Functions | &ByRef • ByVal

24 Apr 2018, 04:04

- I felt that here potentially AssumeByRef could be useful. But then again there's only one function I have that would really benefit. Even then, it may be better as it is.

Code: Select all

;before:
JEE_StrSplitToVars(vText, vDelims:="", vOmitChars:="", ByRef v1:="", ByRef v2:="", ByRef v3:="", ByRef v4:="", ByRef v5:="", ByRef v6:="", ByRef v7:="", ByRef v8:="", ByRef v9:="", ByRef v10:="", ByRef v11:="", ByRef v12:="", ByRef v13:="", ByRef v14:="", ByRef v15:="", ByRef v16:="", ByRef v17:="", ByRef v18:="", ByRef v19:="", ByRef v20:="")

;after:
AssumeByRef JEE_StrSplitToVars(ByVal vText, ByVal vDelims:="", ByVal vOmitChars:="", v1:="", v2:="", v3:="", v4:="", v5:="", v6:="", v7:="", v8:="", v9:="", v10:="", v11:="", v12:="", v13:="", v14:="", v15:="", v16:="", v17:="", v18:="", v19:="", v20:="")

;perhaps BR as an alternative to ByRef?
JEE_StrSplitToVars(vText, vDelims:="", vOmitChars:="", BR v1:="", BR v2:="", BR v3:="", BR v4:="", BR v5:="", BR v6:="", BR v7:="", BR v8:="", BR v9:="", BR v10:="", BR v11:="", BR v12:="", BR v13:="", BR v14:="", BR v15:="", BR v16:="", BR v17:="", BR v18:="", BR v19:="", BR v20:="")

;&?
JEE_StrSplitToVars(vText, vDelims:="", vOmitChars:="", &v1:="", &v2:="", &v3:="", &v4:="", &v5:="", &v6:="", &v7:="", &v8:="", &v9:="", &v10:="", &v11:="", &v12:="", &v13:="", &v14:="", &v15:="", &v16:="", &v17:="", &v18:="", &v19:="", &v20:="")
;I think overall we're better off as we are
- A problem with using & is that I usually copy and paste a function definition into my script to use the function, and then remove any ByRefs. If you use & to mean ByRef and forget to remove it, problem. Also, every time you see someone use an '&' in a function call, is it a 'ByRef' usage that they forgot to replace.

- Btw re. shortening things. We have A_Index, not A_LoopIndex, so why not A_Field? (Btw I would advocate 'as well as', not 'instead of'.)
A_LoopField -> A_Field
A_Space -> A_Sp

- And mentioned here:
objects/object classes: new features from a newbie-friendly perspective - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 83#p214283
Obj.Length() -> Obj.Len() [I find .Length() very hard to type at speed, and even when I use a hotstring, it slows me down. Partly it's because of how 'ngth' are arranged on a qwerty keyboard, and partly it's because of the prevalence of 'ght' words in English causing cognitive conflict.]
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Functions | &ByRef • ByVal

24 Apr 2018, 04:45

lexikos wrote:So are you now saying that the majority of function parameters should specify ByVal, including those that previously specified ByRef only for performance? :roll:
I do not say that. Majority is your assumption. If byref was changed to be default, why would anyone change their now byref parameters to byval? :roll: I made no such suggestion.
Declaring byref do not tell the user that the function intends to change the value,
It should.
Equally, not declaring byval could mean the same thing. And no, it shouldn't, I do not know why you (and nnnik) thinks that. There is no statement in the documentation suggesting that byref is for output or altering a variable more than it is for performance, using byref for perfomance is not some advanced trick.
Regardless, I think the number of parameters declared ByRef for the sole purpose of performance is much smaller than the number of parameters not declared ByRef.
I guess the same, because, 1) it is cumbersome to type it for each parameter which doesn't need to be byval, 2) the word byref is long enough to obscure the view. Both reasons are addressed by the & suggestion. And, I think if byref was default the number of parameters declared as byval for the sole purpose (and need) of protecting the input variables would be even smaller.

Regardless of wheter byval or byref is default, as long as a keyword (or two) to change it is available there is no loss of functionality, neither choice is unreasonable imo.

My final comment on this, I did not make a suggestion to change the current behaviour, I stated my preference.

Cheers :wave:.
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Functions | &ByRef • ByVal

24 Apr 2018, 05:42

Helgef wrote:I do not say that.
I read this:
if a function changes a parameter, and that is not the purpose of the function, it should specify that parameter to be byval.
...as "if changing a parameter is not the purpose of the function, it should specify that parameter to be byval". Assigning a value to a by-val parameter (a local variable) cannot be the purpose of the function, because it would have no effect outside the function (except when the value of that variable affects the result or side-effects of the function, in which case that result or side-effect is the purpose, not the parameter assignment); therefore I interpreted "changing a parameter" as "changing the caller's variable by assigning to the parameter" and did not consider that it could mean anything else.

Parameters which are not currently declared as ByRef are obviously not intended to change the caller's variables, because they cannot. Most parameters of most script functions are not ByRef.

Perhaps you meant that if a function assigns to a parameter but intends for the change to be only local to the function, it should specify the parameter to be ByVal (and otherwise leave it as ByRef). But that means that the author must consciously anticipate the negative consequences of the default behaviour and choose to add ByVal. To me, that seems like the wrong policy.

ByRef by default carries the risk of causing surprising behaviour. ByVal by default has only the cost of copying strings, which is hardly surprising, if you even notice it.
If byref was changed to be default, why would anyone change their now byref parameters to byval? :roll: I made no such suggestion.
Neither did I.
Majority is your assumption.
The majority of function parameters are not being used to change the caller's variable.
And, I think if byref was default the number of parameters declared as byval for the sole purpose (and need) of protecting the input variables would be even smaller.
Yes, and the number of script bugs would increase.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Functions | &ByRef • ByVal

25 Apr 2018, 02:36

lexikos wrote:Perhaps you meant [...]
Yes.
that means that the author must consciously anticipate the negative consequences of the default behaviour and choose to add ByVal. To me, that seems like the wrong policy.
There are two cases where this considerations would need to be made, 1) for internal or private functions, assuming the user is at a level of grasping the concept, this is trivial, 2) for shared functions / unknown caller, same assumption, it is trivial; either use byval (blindly because you cannot know) or instruct the caller to copy its strings if desired. Similarily, passing an object to a function, byref or not, allows the function to modify the object, I do not see too much confusion about it. Although, users using objects has probably at least noticed the existens of the documentation, the same might not be true for all users only dealing with strings.
ByRef by default carries the risk of causing surprising behaviour. ByVal by default has only the cost of copying strings, which is hardly surprising, if you even notice it.
We are back at reading the documentation, which you hinted above, you expect that many do not, this is also part in why I am not actually suggesting this change, byval by default is probably easier for those new to programming (and maybe others). Also, as I said earlier, byref (by default) makes most sense for strings, but parameters can have any type so it is problematic. So I guess numbers would still be byval (by default), but I wouldn't want to amplify the identity crisis of the language by having var:="1" be byval because it is a number :P.
On the other part of the topic, that is &, I have thought about it some more and I think it is an awesome suggestion. It could be added and recommended (in the docs.) to be used instead of the word ByRef when the intention is performance and not outputting a result, then we have a clear way of marking output variables, and a convenient way to avoid unnecessary string copies. Other symbols could be considered for reasons mentioned, eg, $ to mark the saving ;). ByRef could be changed to Out or InOut, I think it is more descriptive and easier to grasp, partly because it is complete words.

Cheers :wave:.

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 37 guests