Bug in BASIC?
Chris Hall (132) 3558 posts |
I have a question about whether the following is a bug: The procedure PROCdmy / PROCdmy2 is provided in an external library so the actual parameters used in the definition are not necessarily known to the user, these should be LOCAL to the procedure so it shouldn’t matter.
I am using the function FNBB("B10") to generate values for x and y, then using those in the procedure call to an external library (which works) but after the library call x has the wrong value. |
Holger Palmroth (487) 115 posts |
My understanding of BASIC V always was that every variable is global, unless explicitly stated in a procedure with the LOCAL statement. |
Colin (478) 2433 posts |
FN/PROC parameters are also local so the x in PROCdmy(x,y,r) is given the value 55 and the value is restored when the procedure ends |
Jeffrey Lee (213) 6048 posts |
Colin is correct – however the important thing to note is that the parameters are local for the duration of the FN/PROC body (as expected) and for the evaluation of the parameters before the body is executed (unexpected compared to most other programming languages). So because PROCdmy accepts an ‘x’ parameter, “ |
Colin (478) 2433 posts |
Yes and if it was considered ‘correct’ behaviour you’d expect
to return 55 and not 1 |
Rick Murray (539) 13850 posts |
And the moral of today’s story: Local names <> Global names ;-) |
Rick Murray (539) 13850 posts |
Just out of interest, I wonder what ABC makes of the above… |
Steve Drain (222) 1620 posts |
In describing BBC BASIC I would rather say: Local values <> Global valuesThere is only one namespace for variables.
This comes about naturally from the above. When a FN/PROC is executed, the current value of a formal paramenter variable is pushed on the stack and the variable is initialised with a null value. Only then is the actual parameter evaluated and the formal parameter variable given its value. Hence that last stage of evaluation will find that if it uses a variable which is also a formal parameter, it will have either a null or unexpected value – see below. Because formal parameters are actioned in turn, an earlier value might be used in a later evaluation, which is even more confusing. ;-) |
Chris Hall (132) 3558 posts |
It is nice to know that it is a feature, not a bug. I shall try to remember not to use a function as an input parameter to a procedure where the function alters a global variable which has the same name as the local parameter variable used in the procedure definition line, as the global variable may be corrupted. This makes it important that when providing a library procedure or function, the acual parameter variable names used in the definition must be explicitly quoted. |
nemo (145) 2554 posts |
I’d counter that this demonstrates one should never use function calls as parameters – it’s never completely safe and is a maintenance nightmare. Inherited localness in subordinate functions is tricky enough when done deliberately. |
Alan Adams (2486) 1149 posts |
This is worrying. When validating input, I frequently use things like A$=FNtrim(FNupcase(A$)) If I read the above correctly, this might result in empty strings. |
Jeffrey Lee (213) 6048 posts |
I believe that the only time you’ll run into problems is if you have a situation like the following: Outer(Inner())
If Inner is invoked anywhere within the evaluation of Outer’s arguments then you’ll encounter problems (e.g. Outer(Middle(Inner())) is dangerous, as is Outer(Middle()) if Middle calls Inner from its body) I think this may also extend to if you directly reference a global (or any other variable from outside Outer’s scope) within the argument list, e.g.: A$="A" B$="B" Outer(B$, A$) ... DEF PROCOuter(A$, B$) I think this will have the effect of calling Outer(“B”, “B”), because the read of A$ that occurs for determining the value of the second parameter will actually be reading the local value from Outer’s call frame. |
nemo (145) 2554 posts |
It’s worse than that Jeffrey. Here’s an example that goes wrong without explicit A$="KO" B$=FNrev(A$):PROCshow(B$):PROCshow(A$) :REM Construction 1 PRINT"Compare and contrast" A$="KO" PROCshow(FNrev(A$)):PROCshow(A$) :REM Construction 2 END DEFFNrev(RETURN A$):A$=RIGHT$(A$)+LEFT$(A$):=A$ DEFPROCshow(A$):PRINTA$:ENDPROC Unless you already knew that those two constructions would behave differently, not when challenged, but without even thinking about it, then you must concede that using function calls as parameters has arcane and complex consequences.
No, it doesn’t work like that. Things get stacked then changed. I say “explicit LOCAL” because |
nemo (145) 2554 posts |
Alan used the example
and suggested
I don’t see why ‘empty’ would be likely. ‘Wrong’ is entirely possible though, as is random memory access, crashing and things catching fire. For example, your The following is robust:
It’s less than 10% slower, and 100% safer. |
nemo (145) 2554 posts |
And if anyone thinks A$="This is an example" REPEATPRINTFNword(A$):UNTILA$="" END DEFFNword(RETURN A$):LOCALZ%,B$ WHILEASCA$=32:A$=MID$(A$,2):ENDWHILE Z%=INSTR(A$+" "," "):B$=LEFT$(A$,Z%-1):A$=MID$(A$,Z%+1) =B$ |
nemo (145) 2554 posts |
… |