LoginLogin

[fixed] Cannot RETURN from GOSUB inside DEF block.

Root / SmileBASIC Bug Reports / [.]

SquareFingersCreated:
Inside a DEF block, it appears GOSUB behaves no differently from GOTO. EDIT: Reported fixed in 3.5.2.

There's a few points of interest in the reference manual that might shed light on this. From section "About DEF user-defined instructions":
- Variables and labels defined in the DEF to END range are handled as local - GOTO outside the DEF to END range is impossible - GOSUB or ON GOSUB in the DEF to END range cannot be used - Then can be used if a SLOT is specified, as in GOSUB "0:@SUB"
Perhaps this is because the RETURN statement is used within a DEF block to exit the block (and in the case of functions, specify a return value).

I had a long post about DEF and CALL on the old site. Some of it was complaints about the manual - which have been fixed now (in both the Japanese and English versions) so I won't repost the whole thing, but the relevant part here is: --- When writing a DEF block: - Other DEF blocks go outside the one you're writing, but can referenced inside. Recursion is not tested for; you can even have a DEF block reference itself, at your own risk. - GOTO commands work as long as the labels they go to are inside your DEF block. - Don't use GOSUB inside a DEF block. The problem here seems to be that GOSUB blocks end with "RETURN" but RETURN has a different meaning inside a DEF block. --- It's interesting to hear that GOSUB will behave like GOTO now; it used to exit the DEF prematurely when it got to the RETURN. EDIT: when I say "Recursion is not tested for," I experimented with that prior to the 3.2.0 update, so it might actually be checked for now (there have been two updates since then). But you used to be able to create infinite loops by having two functions reference each other, etc.

I love how SquareFingers is pointing everything wrong with SmileBASIC; and hes the only one doing it

So I have a question, how is a DEF block any different from, or have any advantage over, a GOSUB event? Basically, I have no idea what's different about DEF, or even how to use it. I get that it has something to do with creating custom events, but wasn't GOSUB able to produce similar results?

So I have a question, how is a DEF block any different from, or have any advantage over, a GOSUB event? Basically, I have no idea what's different about DEF, or even how to use it. I get that it has something to do with creating custom events, but wasn't GOSUB able to produce similar results?
I'm not an expert on smilebasic but I think it works like this: GOSUB jumps to a subroutine and returns execution to the next instruction after GOSUB once that subroutine is fisinhed. When used inside a subroutine, GOTO jumps to the desired subroutine and once it sees a RETURN it ends both that subroutine and the calling one, ignoring all the remaining commands. DEF defines a function, which can also behave like a subroutine but usually takes arguments and returns values. The OP is saying that GOSUB is behaving like GOTO when it's inside a user defined function (not returning to the very next command after GOSUB).

I personally like to use DEF because it feels more modern in the programming world.

One big advantages of DEF over subroutines are how you can pass values to, and get values back from, the code. Say you wrote a subroutine to add two numbers:
@SUBROUTINE_ADD
ADD_OUTPUT=ADD_INPUT1+ADD_INPUT2
RETURN
Now say you want to use it. You want the variable Z to have the sum of the values in X and Y.
ADD_INPUT1=X
ADD_INPUT2=Y
GOSUB @SUBROUTINE_ADD
Z=ADD_OUTPUT
The same code, using DEF as a function:
DEF ADD_FUNCTION (ADD_INPUT1,ADD_INPUT2)
RETURN ADD_INPUT1+ADD_INPUT2
END

Z=ADD_FUNCTION(X,Y)

Four differences between DEF and GOSUB (Here's hoping [code ] is implemented in the foums...) 1. Functions with only one return value can be used in-place, i.e. anywhere you could normally use a single value.
DEF MyFunc(A,B)
  RETURN A+B
END

W=X*MyFunc(Y,Z)
This is the old-fashioned meaning of 'function' in programming terms. See the Instruction List's DEF(3) for more info. EDIT: This is also what SquareFingers is talking about in the post above. 2. When invoking a function with CALL, the function name can be given as a string variable. Thus, if you rewrite a normal invocation:
A=MyFunc(X,Y)
...with CALL:
A=CALL("MyFunc",X,Y)
Then, you could replace the function name itself with a string variable,
MyVar$="MyFunc"
A=CALL(MyVar$,X,Y)
This can be used to call different functions (on the same arguments) without long if/elseif structures. See the Instruction List's entries on CALL for more info. 3. It's easier to pass arguments to functions that are in a separate PRG slot then it is to GOSUB blocks that are in a separate PRG slot. Among other reasons why you might have your code split into two different PRG slots, there's the idea that one slot could act as a kind of game 'engine' which would provide a suite of functions that the code in the main slot could access. This is hampered by the fact that there is no way to globalize variables across PRGs (no, COMMON doesn't do it). You can access gosub blocks in a different PRG slot with GOSUB "n:@MyGosub", where n is a PRG slot 0-3; but if you do this, none of the variables you've got values in from PRG0 can be accessed by the other slot. The use of functions across PRG slots is more well-supported. If you define the function with COMMON DEF, it can be accessed from any PRG slot, and you can pass arguments to it just like you would any other DEF. 4. This last one isn't an 'advantage' so much as just a difference you should be aware of. When you run a program, SmileBASIC scans your entire program for DEF blocks and loads them into memory right away. Thus, errors involving your DEF blocks (for example, two functions with identical names, or one with a missing END) show up immediately upon running the program, instead of only when the offending code is reached. ...then again, it might be an advantage after all: Some speed testing I did earlier suggests (but has not proven) that using functions may actually be faster than navigating around your code with GOSUBs. (I was testing something else at the time so the results aren't clear, but actually it wouldn't be hard to make a test that tests this directly. Maybe I'll try that out tomorrow.)

Ah, okay, thanks. It seems really similar to SUB events in VisualBASIC, which is the only other language I'm familiar with. Definitely looks easier than assigning a whole bunch of variables before GOSUB. Edit: Ninja'd by BeautySoap. Your post kind of confused me, ha. Mainly because I'm not sure what the RETURN A+B meant, or how exactly call works. It is interesting the using DEF may help speed, though, I was actually wondering of that would be the case.

Yeah, CALL is kinda weird and takes some getting used to. That's actually why I showed the 'normal' use and then then version with call, so people could get the jist of it before I went on and threw in the string variable. In any case, the manual (or, the generally more helpful "Instruction List") has some nice examples of DEF and CALL you can check out.

Yeah, CALL is kinda weird and takes some getting used to. That's actually why I showed the 'normal' use and then then version with call, so people could get the jist of it before I went on and threw in the string variable. In any case, the manual (or, the generally more helpful "Instruction List") has some nice examples of DEF and CALL you can check out.
Yeah, by no means CALL is required to use DEFs, nor will most people ever need to use CALL. It's an advanced instruction for calling from a string or smth.

A lot of this post should have gone in "Programming Questions," but since we're here... I thought of one more difference that ought to be mentioned. 5. All variables inside a DEF block are 'local' to the DEF block (so in order to access values from outside, you have to pass them in as arguments). All variables in a GOSUB block are assumed to also exist outside the GOSUB block i.e. are 'global' (so if you want to make them local, just give them unique names.) As a consequence, GOSUB is handy if you need to access a lot of variables that will be the same every time you call the subroutine. DEF is designed to be easier if the variables you need inside your subroutine will be different every time you call it.

All variables inside a DEF block are 'local' to the DEF block
A quick program shows this not to be true:
L=1
TEST
PRINT L
END

DEF TEST
PRINT L
L=L+1
END

That's interesting. The manual does say variables and labels defined inside the DEF block are treated as local, so it does make sense that L in the above example was treated as global. On the other hand:
L=1
PRINT TEST(L)
PRINT L
END

DEF TEST(L)
  RETURN L+1
  L=L+1
END
So, if you add a return value to your function, it suddenly decides that that the L in the DEF block is a new L and treats it as local. Man, that's irritating.

No, it's the fact that you defined the parameter name as L that it's treated as local -- because it is local. Also, declaring a variable with DIM or VAR inside a DEF block will make it local. This is one of the reason OPTION STRICT is useful, because it forces you to declare all your local variables (aside from parameters which are implicitly local).

Oh, thank you. Yes, I see my error now - it wasn't a proper test. As it happens, though,
L=1
PRINT TEST(L)
PRINT L
END

DEF TEST(M)
  RETURN M+1
  L=L+1
END
...still outputs 2 then 1. The operation on L inside the DEF block still didn't affect the L from outside. OPTION STRICT is good to know about. Thanks for pointing that out.

The line L=L+1 in that code never gets executed. RETURN doesn't just set the return value, it also literally returns from the function call.

Oh, I didn't expect that. Why does it require both RETURN and END then? I guess to keep the format the same as when you have a function with no return value or with multiple outputs, so that the DEF block ends with END no matter what. (Just a guess.) Anyway, sure enough, if you reverse the order of the two lines in that block, then L inside is the same as the L from outside. Thanks for teaching me! This has been helpful.

Yes, END is just the equivalent of ENDIF, WEND, NEXT, etc. by marking the end of the block.