LB Booster
Programming >> Compatibility with LB4 >> RIGHT$() etc. with parameter side-effect
http://lbb.conforums.com/index.cgi?board=compatibility&action=display&num=1490284686

RIGHT$() etc. with parameter side-effect
Post by Richard Russell 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.

Re: RIGHT$() etc. with parameter side-effect
Post by Rod 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?
Re: RIGHT$() etc. with parameter side-effect
Post by Richard Russell 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.

Re: RIGHT$() etc. with parameter side-effect
Post by BrianM 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
Re: RIGHT$() etc. with parameter side-effect
Post by tsh73 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.
Re: RIGHT$() etc. with parameter side-effect
Post by BrianM 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
Re: RIGHT$() etc. with parameter side-effect
Post by Richard Russell 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.

Re: RIGHT$() etc. with parameter side-effect
Post by BrianM 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
Re: RIGHT$() etc. with parameter side-effect
Post by Richard Russell on Mar 24th, 2017, 10:51pm

on Mar 24th, 2017, 10:29pm, BrianM wrote:
This has caught me out.

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.

Re: RIGHT$() etc. with parameter side-effect
Post by Richard Russell 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.

Re: RIGHT$() etc. with parameter side-effect
Post by Rod 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.
Re: RIGHT$() etc. with parameter side-effect
Post by Richard Russell 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.