twinbasic / lang-design

Language Design for twinBASIC
MIT License
11 stars 1 forks source link

Return should be legal in Subs #38

Open bclothier opened 2 years ago

bclothier commented 2 years ago

Describe the bug Trying to Return from a Sub raises a "internal error"

To Reproduce Create the code:

    Public Sub Duh()
        Return
    End Sub

It will highlight Return as an error.

Expected behavior No error. It should be legal to return. Had I put in something to return (e.g. Return 1 or Return "foo"), then it's appropriate to show an error since it's a Sub.

BTW, I prefer this over Exit Sub because this avoids the need to change the code should it change from Sub to Function (or vice versa).

Desktop (please complete the following information):

FullValueRider commented 1 year ago

I just realised, using the features discussed above, the return syntax would also be allowable as a keyword for any type of method, because for properties and functions, the value to return on encountering a the return keyword is specified elsewhere.

Greedquest commented 1 year ago

I just realised, using the features discussed above, the return syntax would also be allowable as a keyword for any type of method, because for properties and functions, the value to return on encountering a the return keyword is specified elsewhere.

@FullValueRider I'm confused, your examples don't include the Return keyword? How does that fit in?

fafalone commented 1 year ago

Do we really need a 3rd way to return a value from a function? What benefits does taking over "result" (a breaking-change for thousands of codebases using this common variable name) offer over Return and foo=?

wqweto commented 1 year ago

Instead of result = ... it could be Return = ... not to introduce a separate keyword and to disambugiate from GoSub's Return.

fafalone commented 1 year ago

Why do we need to add the =?

and what would go after the = in a Sub?

Return is already disambiguated; there's no legal syntax in VBx where anything comes after Return. It already works fine in functions. (Forgive me if this is baseless, but is it not universally known here that Return whatever is already legal tB syntax in functions? I doubt that's changing.)

wqweto commented 1 year ago

Using an implicit variable has some advantages. The main one not mentioned above is that you can read it's value i.e. in Foo a statement like Call SomeOtherProc(Foo, 42) is valid.

FullValueRider commented 1 year ago

@wqweto Sorry it was an afterthought. The return would just replace any Exit .... statement as an alternative to the exit .... for those who prefer the different syntax.

fafalone commented 1 year ago

But it wouldn't be a result in Subs, and nobody is talking about removing Function Foo(): Foo = x

Function foo() As Long
        foo = 2
        Debug.Print bar(foo, 2)
    End Function
    Function bar(x As Long, y As Long) As Long
        Return x + y
    End Function

This is already legal and prints the expected 4.

FullValueRider commented 1 year ago

@fafalone Its not a third way. There would be no need for 'return Value' syntax so that new usgage in twinBasic could be retired before V1, a perfectly legitimate change. The proposals I made were to try and resolve the issue around the use of return in a Sub.

The = is needed as I envisage result (or whatever name is used) being an alias for the Method name variable.

bclothier commented 1 year ago

Oh, my.

This thread is sprawling far beyond what should be the original scope. Some of the points sounds like they are a separate proposal, and probably should be dealt in their own issue, and other points seems to be taking a different approach altogether.

Let's recap:

VBx currently:

Duh: Debug.Print "Ha-ha!" End Sub


While I seriously doubt there's anyone trapping for run-time error 3 `Return without GoSub`, I hope that shows how we might have a VB codebase that seems to work because it's swallowing the run-time error 3 and nobody has noticed.

tB currently:
* Support the vB syntax for backward compatibility
* In addition, provides a `Return <value>` for a function which both assigns a value and leaves the function immediately. 
* Does not allow `Return` (without a value) in a `Sub` for the backward compatibility with `GoSub...Return` syntax which is also legal. 

Other languages tend to follow the same convention where:
* `Return` usually leave the method immediately.  For "sub" (aka `void` method), `return` without value is used to leave the method.
* ...unless it is inside a `try...finally` block. In this case, the `finally` block is executed, thus guaranteeing that the code within the `finally` _always_ execute regardless whether a value was returned or an exception was thrown. 

tB currently doesn't have a `try...catch...finally` idiom but it will be provided via vbWatchDog once it is made available in tB. This is why error handling is relevant to the discussion because `Return` should not bypass the finally block and make it easier for programmer to avoid accidentally circumventing this by using `Exit <procedure>` instead of `GoTo <label>`. Changing the behavior of `Exit <procedure>` to always execute the finally block is potentially more problematic and may break backward compatibility in subtle ways even if we used a new keyword for the `finally` block. Using `Return` with the expectation that it will always execute the `finally` block is a preferable and safer way of enhancing the language in both how it handles the return value and error handling. 

The ideal and simplest outcome would be to just allow `Return` as a legal syntax for use in `Sub`. This might be accomplished by either 1) disallowing `GoSub...Return` or 2) no longer raising runtime error 3 but instead exiting the procedure, which is a change in the behavior (e.g. the sample code shown above will no longer print `Ha-ha!`; if there's an OERN it might be even more drastic). I personally would rather prefer having simple `Return` in a `Sub`.

The other idea of `Return Default` (or a variant thereof) was meant to be an alternative just in case simple `Return` is not practical for some reasons that I might be unaware of. 

Using `Exit Sub` instead of `Return` means there's two different ways of doing the same and runs into the problem of cleanup as I described above. By having a `Return` or variant thereof in `Sub`, we can then expect the same behavior with `try...finally` when dealing with `Return`. 
wqweto commented 1 year ago

Now I figured out something which was not apparent immediately. With Return Default syntax in a sub the compiler can disambiguate from original Return statement while having Return Default working in a function is just a non-asked byproduct.

I'm all for having plain Return working in a sub without GoSub statements and disallowing it in a sub with GoSub so that bad style in past can have some consequences along the road :-))

Greedquest commented 1 year ago

The only fully back compatible solution I can think of is to have the Return keyword to exit a sub (/ function, for consistency) only enabled in .twin files, and in .bas/.cls files etc the error 3 is raised.

bclothier commented 1 year ago

Hmm, that entails that for consistency, the .bas/.cls files wouldn't have other tB features (c.f. #13 ). I don't think that's the case currently. I'm not sure how I feel about the idea of having the file extension enabling/disabling the language features.

Greedquest commented 1 year ago

Have we considered using Exit instead of Return

Sub Foo()
    Exit
    Debug.Print "not called"
End Sub
Function Bar() As String
    Exit "return value"
    Debug.Print "not called"
End Sub

Or some combination with With, As e.g.:

Function Bar() As String
    Exit With "return value"
    Debug.Print "not called"
End Sub
Greedquest commented 1 year ago

I'm not sure how I feel about the idea of having the file extension enabling/disabling the language features.

@bclothier Agreed I'd rather not, but it's also sort of my suggestion for implicit namespaces; public code in .bas files goes into the global namespace but public code in Modules defined in .twin files is contained in a namespace for that file to add better encapsulation.

It means we can 100% guarantee an easy migration to tB with all edge cases, and then moving to .twin files can sidestep some small fraction of edge cases where the value is massive and the likelihood of this affecting any real code is minimal

Greedquest commented 1 year ago

Changing the behavior of Exit <procedure> to always execute the finally block is potentially more problematic and may break backward compatibility in subtle ways even if we used a new keyword for the finally block

I disagree. If we use vbWatchdog as-is then yes. If instead a new Try... Finally...End Try block is introduced then no VBx code will already be written in that scope, since it would be a syntax error, so we can define Exit <procedure> to call finally without breaking any backwards compatibility I believe.

bclothier commented 1 year ago

Have we considered using Exit instead of Return

This would seem to avoid the compatibility issues though TBH I'm ambivalent about overloading the word Exit to mean to return a value which would make the language even more odd compared to other languages that uses the return keyword.

Also, from my POV, I find it mildly annoying that if you had a situation where you had to alter the procedure from Sub to Function (or vice versa), the End <procedure> was automatically updated but not the Exit <procedure>. @fafalone has pointed out that it could be something that IDE could make it easier to do. Then there's the fact that we have multiple ways of doing same thing (e.g. Exit Sub or Exit for subs, Return <value> for functions) which seems to me unnecessary distinction. I find it easier when I can use Return everywhere without going "oh, that's a sub, I should be using Exit" and backspace then re-type.

If we were to implement Exit <value> or Exit With <value> with corresponding Exit for subs, I can see how that might lead to subtle logic errors entering Exit Function when it should have been Exit With <value> or vice versa, which means more bugs and therefore a frustrating language to use. I think we want to avoid that, too.

I disagree. If we use vbWatchdog as-is then yes. If instead a new Try... Finally...End Try block is introduced then no VBx code will already be written in that scope, since it would be a syntax error, so we can define Exit <procedure> to call finally without breaking any backwards compatibility I believe.

Just to point this out, we have an open issue on Try/Finally as a keyword ( #61 ) which also hits on the issues we already have to deal with the different keywords & strategies for leaving a routine and error handling which as I explained over there is ironically errorprone.

Anyway, to your point; I need to ask - is it a good idea to alter the behavior of the Exit <procedure> so that it always calls the Finally block? Personally, I think yes and for everybody's sanity, it ought to but the backward compatibility may be harmed by this subtle change in the behavior. Using Return at least makes this an explicit opt-in and we can then have IDE encourage the use of Return over Exit <procedure> as a hint to help promote good coding practices, rather than forcing them to choose a legacy mode because they can't be sure that it will work as-is when they import their original VBx codebase.

fafalone commented 1 year ago

I'd be against modifying Exit <method> to jump to what's basically a label. Keywords that have different meanings in different contexts are a nightmare for language clarity, and are beginner-hostile substantial increases in learning curve. It's been a while since I used a language with this syntax, but isn't the traditional solution for this Exit Try? That's both far more clear, consistent with all the existing VB Exit syntax like Exit For or Exit Do, and has no impact on backwards compatibility at all as it's not legal syntax in VBx.

bclothier commented 1 year ago

I think we're talking about different things.

Consider this hypothetical pseudo-code:

Private Sub Fizz()
  Try
    OpenHandle
    Exit Sub
  Finally
    CloseHandle
  End Try
End Sub

The question is if there's a Exit Sub inside the Try block, should it execute the Finally block? In my opinion, yes it should. Otherwise, we destroy the value of the try...catch...finally block. However, I'm uncertain if there's a problem with breaking the backward compatibility if we change that behavior. Furthermore, what I dislike is that the behavior changes for the Exit <procedure> just because it's inside a try...finally block versus being not inside one. Being able to use Return and thus having a consistent behavior (e.g. being assured that Finally block will always execute even if we leave the routine early) and having IDE highlight a good coding practice seems preferable than overloading the Exit keyword and giving it new meaning that might be not as apparent.

fafalone commented 1 year ago

We're not talking about different things, I'm aware you meant if it was inside the Try block.

That's what I meant-- you're changing the meaning of Exit <method> based on where it is, which brings all the downsides I mentioned. If you want to get out of the Try block while executing Finally, you should need to use Exit Try or perhaps a new keyword after Exit could be added, like Exit Sub With Finally only more concise.

I'd say that yes, it's breaking backwards compatibility, because you're changing the meaning of existing code simply by virtue of someone wanting to add a Try block. I don't understand your statement that to not exit the procedure immediately is altering the behavior... there's no scenario in VBx where Exit <procedure> does anything other than exit the procedure immediately, but here you're proposing that it should.

This doesn't destroy the value of the Try block at all, because nothing stops someone from not using Exit <procedure> and instead using the proper Exit Try. I'd think if someone is going out of their way to add a Try block, they'd want Finally to execute so wouldn't use syntax that bypassed it. Being allowed to use syntax that bypasses it doesn't destroy the value.

bclothier commented 1 year ago

My point is that when inside a Try block, it should not be possible to leave the block without executing the Finally block where present. With Exit <procedure> as-is, that would bypass the Finally execution, which makes it superficial and put us back to square one WRT having to remember to use right syntax in right place which I think is not user-friendly as I explained in the linked issue #61 . Requiring using Return and disallowing the use of Exit <procedure> inside the Try block would at least avoid that problem without changing the behavior of the Exit <procedure> or alternatively do an Exit Try, then Exit <procedure> outside the block.

Note that the same problem would exist with GoTo <label> which allows us to leave any other blocks which feels totally wrong to me. The 90% of the value with the Try block is being guaranteed that the Finally will run, errors or no errors. Otherwise, it's just a keyword posing as a label.

fafalone commented 1 year ago

I really can't see how having to figure out that Exit <procedure> takes on an entirely new meaning if you use it inside a Try block makes the syntax less difficult to understand. IMHO it's just not user-friendly to have context-dependent alternative meanings for "Exit" like "If Exit is within Try, Exit doesn't mean exit, Exit means GoTo Finally, Then Exit".

I think the value in Try is in allowing the user to have Finally always run, error or no error. I don't think the value of forcing them into it by breaking backwards compatibility and creating context-dependent alternative behavior for long-standing, well known core language syntax outweighs the substantial drawbacks of that.

Mind you I would, as mentioned above, support a different Exit syntax that would execute Finally before exiting-- that's the best of all worlds. Allows opting into the benefits without breaking things to force it.

FullValueRider commented 1 year ago

I can't see the point in arguing about try/catch in the context of a return syntax. In VBA, vbWatchdog was constrained by the need to work within existing VBA code. With twinBasic, try/catch/finally syntax can be added as new twinBasic syntax. The only constraints on such an addition being any potental conflicts between code implementing vbWatchdog try/catch and any new try catch syntax.

Greedquest commented 1 year ago

@bclothier

Anyway, to your point; I need to ask - is it a good idea to alter the behavior of the Exit so that it always calls the Finally block? Personally, I think yes and for everybody's sanity, it ought to but the backward compatibility may be harmed by this subtle change in the behavior.

@fafalone

[Using Exit in a try block would be] breaking backwards compatibility and creating context-dependent alternative behavior for long-standing, well known core language syntax outweighs the substantial drawbacks of that.

Apologies if I'm missing something here or misinterpreting. I'm confused by the idea of calling this "altering the behavior" of Exit or it posing a risk to backwards compatibility. Right now "Exit Sub" or "Exit Function" to me means not just jump out, there is also clean-up of COM objects calling Class_Terminate and probably clearing memory of variables.

The behaviour of "Exit " is not defined inside a try-catch-finally block because it doesn't exist in VBA yet, so we can't be "changing" the behaviour and breaking backwards compatibility, the behaviour is yet to be defined. And having Exit <Procedure> mean "Exit and perform any clean-up; such as memory, Class_Terminate and/or Finally blocks" is very unsurprising and the precedent from all other languages with early return syntax and finally blocks.

There is no backwards compatibility to break because this:

Function Foo()
     Try
         Exit Function
     Finally
          ' Clean up before exit
     End Try
Exit Function

...is not valid code yet. So there is no backwards compatibility to consider. There is "expected behaviour" to consider, and designing features to feel idiomatic in VBA and unsurprising. But you're not going to break any existing code by defining a finally block to run on an early Return or Exit, because no code exists yet to break. Or am I missing something?

@bclothier is right in that the value of a finally block is that it always runs regardless of what happens inside the try block. To free resources or close context managers.


note I'm ignoring vbWatchdog which I believe uses labels rather than keywords to mark the regions of code for a finally block, although I haven't used it myself. I'm assuming keywords will exist

DaveInCaz commented 1 year ago

@Greedquest

And having "Exit " mean "Exit and perform any clean-up, either memory, Class_Terminate or Finally blocks" is very unsurprising and the precedent from all other languages with early return syntax and finally blocks.

FWIW I had the same instinct, everyone has expectations for Finally from other languages. Would be non-controversial (IMO) to do the same, and potentially confusing or error-prone not to.

bclothier commented 1 year ago

Apologies if I'm missing something here or misinterpreting. I'm confused by the idea of calling this "altering the behavior" of Exit or it posing a risk to backwards compatibility. Right now "Exit Sub" or "Exit Function" to me means not just jump out, there is also clean-up of COM objects calling Class_Terminate and probably clearing memory of variables.

Those are handled by the compiler whereas the Finally points to user-defined code. In VBx code, you'd have to use GoTo <label> orResume

The point is that if Exit <procedure> is allowed inside a Try block, the behavior will be different - it must be in order for Finally to be guaranteed to be executed. This is a change in the expectation because in VBx code, one could (ab)use Exit <procedure> to leave a routine without even calling the cleanup routine. Personally, I think that's not good coding but it is legal code and I've seen people (ab)use that feature as to avoid doing additional work in the cleanup. Example:

Public Sub Foo()
  On Error GoTo ErrHandler

  If Bar() Then Exit Sub

  'Do Foo

  If Foo_d Then 
   GoTo ExitProc
  End If

  'Foo some more

ExitProc:
  'Unfoo
  Exit Sub
ErrHandler:
  Debug.Print "Bonk!"
  Resume ExitProc
End Sub

Now suppose we then update the procedure and put in a Try:

Public Sub Foo()
  Try
    If Bar() Then Exit Sub

    'Do Foo

    If Foo_d Then 
     GoTo ExitProc
    End If

    'Foo some more
  Catch
    Debug.Print "Bonk!"
  Finally
    'Unfoo
    Exit Sub
  End Try
ExitProc:
End Sub

What's the behavior of Exit Sub and GoTo ExitProc?

You are correct that right now, the behavior of Exit <procedure> inside a Try block is undefined but the concern is keeping thing consistent so that users are not confused or surprised by the changes in the behavior. That's going to be important when they start updating their existing VB6 codebase in the ways I illustrated above.

In this view, I think I'm arguing that Exit <procedure> and GoTo <label> (possibly all GoTo and GoSub) probably should be disallowed and be a compiler error inside the Try block. As I said earlier, we can have IDE hint at using Return and Try block as the preferred syntax to help promote good coding practices. This also argues against the idea of using just Exit or Exit With because it'll have the problem of being too similar to the Exit <procedure> but not quite that it's off-putting. It might be me but I gave up on VB.NET exactly for that reason -- there were too many "this is just like VBA! No, wait.... it's not." mindtraps that it was difficult to be productive in VB.NET. I'd rather that tB avoid that kind of mindtrap.

note I'm ignoring vbWatchdog which I believe uses labels rather than keywords to mark the regions of code for a finally block, although I haven't used it myself. I'm assuming keywords will exist

Just FYI. In VBx's vbWatchDog, the Exit Sub will not call the finally block. If one wants the Finally to always run, one has to use ErrEx.DoFinally instead of Exit <procedure>:

Private Sub CallIt()
    DoIt
End Sub

Private Sub DoIt()
    Exit Sub
ErrEx.Finally
    Debug.Print "Finally!" 'not printed
End Sub

I should note that I've made boneheaded mistakes like this:

Private Sub Derp()
  Debug.Print "Derping"
ErrEx.DoFinally 'Oops! Should be ErrEx.Finally! 
  Debug.Print "Derped" 'not printed
End Sub
DaveInCaz commented 1 year ago

People only have to convert their VBx code to tB once, but then they have to maintain it forever after. It seems better to me to err on the side of having a well behaved language feature vs. making the transition to tB a little easier. Otherwise the programmer will pay for that many times over instead of just once when they update to tB.

Personally I would expect that the Goto would not circumvent the Finally. Either that, or Goto within a Try to a label outside the Try should just be disallowed; but that seems less useful.


@bclothier thought experiment... If hypothetically there were an IDE refactoring that would transform from your first example to the second... that could indeed cause some surprises. Perhaps having the refactoring add a warning and/or comment about how it affects goto / exit would be a good idea in that case. The VS2010 VB migration assistant would very liberally add warnings like that. (E.g., for array bounds change from 1- to 0- based).

bclothier commented 1 year ago

This was supposed to be an example of refactoring by hand. If IDE were to provide a refactor, it'd be probably smarter than that and substitute in Return or Return <value> and removing all GoTo or Exit <procedure> as well as any labels so that there is no ambiguity over how the code leaves the procedure and is guaranteed to always leave the procedure in a consistent and well-defined manner. I just don't think it's good idea to mix the paradigms of gotos/exits with try/catch/finally.

The more recent languages avoid this problem by simply never giving the equivalent of GoTo in its syntax so that issue is a moot point in those languages; it's impossible to jump out in an uncontrolled manner. Because tB has the commitment to backward compatibility, this is not a moot point.

fafalone commented 1 year ago

Apologies if I'm missing something here or misinterpreting. I'm confused by the idea of calling this "altering the behavior" of Exit or it posing a risk to backwards compatibility. Right now "Exit Sub" or "Exit Function" to me means not just jump out, there is also clean-up of COM objects calling Class_Terminate and probably clearing memory of variables.

I don't think hidden non-user code is a reasonable comparison here. VB programmers don't think about, or consider, hidden codepoints, whereas we very much expect consistent behavior in user code flow. Hidden code isn't subject to all the random errors and bad practices of user code; there's no world where everyone writes flawless finally blocks that don't trigger errors.

The behaviour of "Exit " is not defined inside a try-catch-finally block because it doesn't exist in VBA yet, so we can't be "changing" the behaviour and breaking backwards compatibility, the behaviour is yet to be defined. And having Exit mean "Exit and perform any clean-up; such as memory, Class_Terminate and/or Finally blocks" is very unsurprising and the precedent from all other languages with early return syntax and finally blocks.

This also seems entirely unreasonable. Code doesn't cease to the VB6 language just because you've put it inside another type of code flow control block. This is like saying we can throw all the rules of the language right out the window because of some unrelated label. It's absolutely breaking backwards compatibility to change the meaning of existing code, end of story. It doesn't matter that it's a Try block any more than an If block, VBx code needs to execute as defined by VBx code in order to be compatible. If you want to argue for breaking backwards compatibility, ok, but let's not pretend this isn't what we're talking about.

@bclothier is right in that the value of a finally block is that it always runs regardless of what happens inside the try block. To free resources or close context managers.

And I for one certainly look forward to taking advantage of that value. I don't care to have it forced upon me by breaking the rules for existing syntax. I'll personally, in forseeable cases, not be using Exit <procedure> inside my Try blocks, because I do want Finally to run. But if, for some reason, I did want to break out without running Finally, I would not want to be in a situation where the language has forbidden that by altering the behavior of existing syntax.

If we are going to handcuff users, I much prefer Ben's alternative of disallowing Exit <procedure> inside a Try block.

People only have to convert their VBx code to tB once, but then they have to maintain it forever after. It seems better to me to err on the side of having a well behaved language feature vs. making the transition to tB a little easier. Otherwise the programmer will pay for that many times over instead of just once when they update to tB.

This was .NET's philosophy; I'm using VBx and tB because I prefer that language, despite it's shortcomings, over "better" languages, for the uses where it's practical (and even somewhat impractical, if it's possible).

DaveInCaz commented 1 year ago

... It's absolutely breaking backwards compatibility to change the meaning of existing code, end of story.

That seems to be 100% accurate - the essential definition of what "backwards compatibility" probably means to most people.

But once you alter your code deliberately, why would it be expected to behave the same way? Alteration could be updating it to use a tB language feature that did not exist in VBx, or anything else. I don't think this is the sense in which "backwards compatibility" really applies.

bclothier commented 1 year ago

But once you alter your code deliberately, why would it be expected to behave the same way?

Keep in mind that typically in a migration project, it's seldom all at once. More likely, they'll want to keep everything as-is and update only this and that and gradually update stuff as they go, which helps amortize the development costs. That's the biggest win with the backward compatibility promise of tB that other languages can't even begin to fulfill.

mansellan commented 1 year ago

Can't we just generalise Exit, so that it doesn't need to be told what it's exiting (other than it not being a loop)? Presumably for functions and property-gets, it just returns the default?

How about Exit Procedure, which is equivalent to Exit [Sub|Function|Property], without having to be explicit?

bclothier commented 1 year ago

Don't think Exit is quite basic-y when you consider the block structures:

Public Sub Foo()
  Dim i As Long
  If True Then
    For i = 0 To 0
      Exit For
    Next
    Do
      Exit Do
    Loop
  Else
    Exit If 'Not a real keyword, included for illustration
  End If
  Exit Sub
End Sub

Compare with a C-family pseudocode:

void foo() {
  if(true) {
    for(var i = 0; i < 1; i++) {
      break;
    }
    while(true) {
      break;
    }
  } else {
    break;
  }
}

The appeal of BASIC is that you can at least identify what type of blocks you're closing. A generalized Exit & End would start to resemble more like a verbose form of C-family language and probably even more confusing:

Public Sub Foo()
  Dim i As Long
  If True Then
    For i = 0 To 0
      Exit
    Next
    Do
      Exit
    Loop
  Else
    Exit
  End
  Exit
End

So yeah, I don't think a generalized form will work very well.

Exit Procedure would be a possible alternative.

I find it interesting that you cannot break nor continue out of a non-loop structure. You can only return in this case case. tB does have Break and Continue (yay!), but not sure I'd want to overload it in this way.

mansellan commented 1 year ago

Sorry, my second paragraph was disconnected from the first. I guess I initially envisaged a general Exit, but then saw the problems you detailed.

At this point, I'm only proposing Exit Procedure, which would be equivalent to Exit Sub, Exit Function, or Exit Property, depending on context.

mansellan commented 1 year ago

Wait...

How about, in a Sub, you can Return Nothing? If you change a Function to a Sub, you'd have to change the return, but maybe that's a good thing? In that you have to think what that change means? C# does that...

bclothier commented 1 year ago

FWIW, I think it comes close to Return Default proposed earlier in this thread. The thing with Nothing is that it's for an object which isn't always appropriate. Return Empty might be closer.