Author |
Topic: RIGHT$() etc. with parameter side-effect (Read 422 times) |
|
Richard Russell
Administrator
member is offline
Posts: 1348
|
|
RIGHT$() etc. with parameter side-effect
« Thread started on: Mar 23rd, 2017, 3:58pm » |
|
Triggered by a discussion at the BBC BASIC forum I tried this program in LB 4 and LBB:
Code: global b$
b$ = "good"
print right$(b$, four())
end
function four()
b$ = "bad"
four = 4
end function LBB prints 'good' but LB4 prints 'bad'. My personal opinion (as is evident from my choice of strings!) is that the LB 4 answer represents a bug - the side-effect of the function should not affect the result from right$() because "good" has already been passed as the first parameter - but your judgment may vary.
Worth noting the difference anyway.
Richard.
|
|
|
|
Rod
Full Member
member is offline
Gender:
Posts: 110
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #1 on: Mar 23rd, 2017, 8:47pm » |
|
Since everything is sequential we first pass global b$, "unnecessarily", in the function call to right$(). Then we call four() and as expected global b$ is available and is changed to "bad" globally. At which point the last sequential action is to return the right most value of global b$.
If the discussion is about b$ within the function being unique and not global then I get confused. Global is global?
Passing global values in any function should get a warning?
|
|
Logged
|
|
|
|
Richard Russell
Administrator
member is offline
Posts: 1348
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #2 on: Mar 23rd, 2017, 9:53pm » |
|
on Mar 23rd, 2017, 8:47pm, Rod wrote:At which point the last sequential action is to return the right most value of global b$. |
|
That's not how functions in Liberty BASIC are supposed to work (which I think you know, really). The parameters to an LB function are passed by value, not by reference, so the first parameter to the RIGHT$() function in the code I listed is the string "good", it's not the variable b$! The fact that the variable b$ gets modified at some subsequent point is irrelevant, what matters is the value of the first parameter at the time the function is called.
Hence the correct result is "good" as it is in LBB. In a language which passes parameters by reference (like QBASIC) you can argue that the subsequent modification of b$ should have an effect, but that's not the case in LB.
Quote:Passing global values in any function should get a warning? |
|
Why? Whether a variable is global or not has no bearing on its suitability as the parameter of a function. One of the valuable LBB extensions is to be able to pass a whole array as a parameter of a SUB or FUNCTION, but in Liberty BASIC arrays are always global!
Consider a trivial function which returns the square of a number. There's no reason to think you wouldn't want to find the square of a global variable; this works equally well in LB 4 and LBB of course:
Code: global Alpha, Beta
Alpha = 1.2
Beta = 3.4
print square(Alpha), square(Beta)
end
function square(n)
square = n * n
end function Richard.
|
|
|
|
BrianM
New Member
member is offline
Posts: 15
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #3 on: Mar 24th, 2017, 8:45pm » |
|
It would appear that LB evaluates function calls in the parameter list before 'normal' parameters. Try running ...
Code:
global b$
b$ = "good"
print right$(b$, four())
b$ = "good"
print right$(nochange$(b$), four())
end
function four()
b$ = "bad"
four = 4
end function
function nochange$(s$)
nochange$= s$
end function
You should get the output (in LB 4.04) :-
bad good
This seems wrong to me. Either evaluate parameters consistently left to right (sensible) or right to left (a bit odd) but not function calls first.
As a general rule it is bad practice to update global variables in a function.
Brian Matthews
|
|
Logged
|
|
|
|
tsh73
Full Member
member is offline
Gender:
Posts: 210
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #4 on: Mar 24th, 2017, 9:03pm » |
|
Quote:As a general rule it is bad practice to update global variables in a function. |
|
Huh? Where should I update them then?
But really if my code written so it depends from order of evaluation, I'm in trouble.
|
|
Logged
|
|
|
|
BrianM
New Member
member is offline
Posts: 15
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #5 on: Mar 24th, 2017, 9:30pm » |
|
Ideally a function should calculator a value from the given parameters and should not change anything else (see pure functions on Wikepedia). This avoids unintended side effects. If it calculates a value and performs other processes then to my mind it should not be a function but a subroutine. Obviously rules are made to be broken and there will always be instances where it is pragmatic to update global or (preferably) static variables. For example in the implementation of iterators.
I agree with you in that LB is rather bizarre in the order of evaluation of parameters.
Brian
|
|
Logged
|
|
|
|
Richard Russell
Administrator
member is offline
Posts: 1348
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #6 on: Mar 24th, 2017, 10:01pm » |
|
on Mar 24th, 2017, 9:03pm, tsh73 wrote:Huh? Where should I update them then? |
|
If you want to change a 'global' variable in a function it's arguable that you should instead make it an ordinary variable and pass it as a BYREF parameter. True 'globals' should probably be reserved for constants (or at least values that are changed only in the 'main program').
But Liberty BASIC programs rarely achieve the highest standards of program design, not least because certain features of the language (such as arrays and structures always being global) make it impossible.
Richard.
|
|
|
|
BrianM
New Member
member is offline
Posts: 15
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #7 on: Mar 24th, 2017, 10:29pm » |
|
Quote:If you want to change a 'global' variable in a function it's arguable that you should instead make it an ordinary variable and pass it as a BYREF parameter. |
|
I agree but beware. LB implements BYREF as a copy on entry to the sub/function and a copy back on return. It does not pass the address of the variable. Thus if you update a global variable as a parameter in a sub/function the global variable itself is not updated until the sub/function is exited. This has caught me out.
Brian
|
|
Logged
|
|
|
|
Richard Russell
Administrator
member is offline
Posts: 1348
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #8 on: Mar 24th, 2017, 10:51pm » |
|
on Mar 24th, 2017, 10:29pm, BrianM wrote: It wouldn't catch me out - BBC BASIC has always worked the same way and it's therefore what I expect. Mind you BBC BASIC is more honest about the terminology: it uses 'RETURN' rather than 'BYREF' to emphasise that it's not an address being passed but that the parameter is updated on return.
Richard.
|
|
Logged
|
|
|
|
Richard Russell
Administrator
member is offline
Posts: 1348
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #9 on: Mar 24th, 2017, 11:09pm » |
|
Not surprisingly the same thing happens if you use BYREF rather than a global:
Code: b$ = "good"
print right$(b$, four(b$))
end
function four(byref a$)
a$ = "bad"
four = 4
end function Richard.
|
|
Logged
|
|
|
|
Rod
Full Member
member is offline
Gender:
Posts: 110
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #10 on: Mar 25th, 2017, 10:58am » |
|
I can't see how the right$() function can be enacted without the four() function being processed first. If that function changes either a global variable, or byref, a passed variable that will happen during the function call or on its return.
I take the point that the variable may be passed as a copy but I am not sure that a copy is passed when the variable is global in LB. However, changing the variable in the function should result in the updated variable being sliced. Just my opinion, as the code can't mean to accomplish anything else.
But as has been said, there are easier ways to trip up.
|
|
Logged
|
|
|
|
Richard Russell
Administrator
member is offline
Posts: 1348
|
|
Re: RIGHT$() etc. with parameter side-effect
« Reply #11 on: Mar 25th, 2017, 2:09pm » |
|
on Mar 25th, 2017, 10:58am, Rod wrote:I can't see how the right$() function can be enacted without the four() function being processed first. |
|
I'm sure nobody is suggesting otherwise: of course all parameters must be 'evaluated' (i.e. converted to a numeric or string value) before being passed to the function that needs them. That's inherent in being 'passed by value'.
The point is that they should be evaluated in a 'sensible' and 'predictable' fashion, for example consistently left-to-right. Evaluating all the 'function' parameters before any of the 'scalar' parameters, which appears to be what LB does, is unexpected and breaks the 'rule of least astonishment'.
Quote:I am not sure that a copy is passed when the variable is global in LB. |
|
Two points to make there. Firstly whether the variable is global or not should not make any difference to the way it is passed to a function. In LB a global variable is one which is 'in scope' everywhere: both in the main program and within SUBs and FUNCTIONs. It is no less suitable as a parameter of a function than any other variable (that's not to say LB 4 doesn't treat globals differently: it does lots of peculiar things such as this bug!).
Secondly, as I demonstrated in a recent post, the problem persists in the absence of any global variables - using BYREF triggers exactly the same behavior.
One has to 'expect the unexpected' when using LB 4 because it has so many bugs and strange features. For example, also in respect of passing parameters to a function, this bug demonstrates that parameters aren't always passed 'by value' even though the documentation clearly states that they should be (in the absence of a BYREF).
If I'd realised just how much the actual behavior of LB 4 differs from what the documentation states, making it all but impossible to emulate accurately, I might not have set out to create LBB in the first place!
Richard.
|
|
Logged
|
|
|
|
|