And the reason why GOSUB was an anti-pattern was not because it was local, but because it was so low-level. It was not really a function call - it changed the current instruction pointer to a new statement in the same function (so you didn't get a new execution frame etc), and pushed original instruction pointer onto its own internal stack, from whence you could then return to (effectively, GOTO) it with the RETURN statement. Because it had no execution frame, it also had no provision for passing arguments. And because it was the same code, there were no boundaries - you could flow into the section of code that was meant to be GOSUB'ed into without the use of GOSUB, and you could flow back out without RETURN. E.g.:
foo = 1
IF foo THEN GOSUB bar
PRINT "No GOSUB"
bar:
PRINT "Inside bar"
IF foo THEN foo = 0: RETURN
PRINT "No return"
On the first run, this will do GOSUB, you'll see "Inside bar", then the flag is reset and we return to the point where GOSUB is invoked. But then the natural code flow will pass the "bar" label, and all that code is executed again, except this time it doesn't RETURN, but instead just keeps flowing on.So, basically, there's an utter lack of structure here. There's no matching of GOSUB and the corresponding RETURN - indeed, there may not even be a corresponding one, or there may be many, and the same RETURN can be reached via GOSUBs through different labels (and will return to wherever the GOSUB came from). If you RETURN without any GOSUB on the return stack, that's a runtime error, not a compile-time one. If you GOSUB without a RETURN, it's not an error, but you will eventually overflow the stack if you keep doing it. And so on.
So, it's a very messy mechanism that dates back to pre-structured days of BASIC. That's the only reason why it was discouraged why BASIC got SUB/FUNCTION.
public void Test()
{
// Doesn't work
Func<A, string> lam = (A a) => a.ToString();
// Works
string LocalFunc<A>(A a) => a.ToString();
}
Also I think if the local function doesn't close over any variables in the containing method then there's no additional memory allocation, whereas the lambda itself must be allocated.