dotnet / vblang

The home for design of the Visual Basic .NET programming language and runtime library.
288 stars 66 forks source link

Suggestion: Allow a statement after a method declaration (Remove BC30040) #477

Open gilfusion opened 5 years ago

gilfusion commented 5 years ago

Currently, this is valid:

Sub WriteFoo()
    Console.WriteLine("foo")
End Sub

and this is valid:

Sub WriteFoo()
    Console.WriteLine("foo") : End Sub

but this is an error:

Sub WriteFoo() : Console.WriteLine("foo") : End Sub

If a method only has one short line of code in its body, why not make it possible to shorten the whole method to one line?

More often I notice this, though, with properties, where there is validating code in the Set accessor, meaning I cannot use an autoproperty, but I still need to use 2-3 lines of code for the Get accessor, because while this is valid:

Property Bar As Integer
    Get
        Return _Bar
    End Get
    Set
        If Value < 0 Then Throw New Exception
        _Bar = Value
    End Set
End Property

and this is valid:

Property Bar As Integer
    Get
        Return _Bar : End Get
    Set
        If Value < 0 Then Throw New Exception
        _Bar = Value
    End Set
End Property

this is an error:

Property Bar As Integer
    Get : Return _Bar : End Get
    Set
        If Value < 0 Then Throw New Exception
        _Bar = Value
    End Set
End Property

Sub and Function declarations can be long, so I can see a side-scroll reduction benefit to disallowing statements after them, but requiring Get, which can only be that one word, to be on a line by itself seems a bit much.

If expression-bodied members (#61) don't make the cut for VB, this seems like a reasonable compromise.

Are there any specific reasons for the current behavior? Is it required to make parsing work correctly (or is it an artifact of making parsing work correctly pre-Roslyn), or is there a language-design rationale for it?

VBAndCs commented 5 years ago

Great note. I think there is a great opportunity here to have bodyless methods in VB.NET like this:

Sub WriteFoo() : Console.WriteLine("foo") 

This will not break older code since it is forbidden as you said. So, there is no need to the : end sub part. There are many requests for such a thing in different syntax, but it seems you found the perfect one.

AdamSpeight2008 commented 5 years ago

Needs looking into as it may be an issue with events handles and implements.

Sub Method() Implements IThisInterface.Method : Console.WriteLine("foo")

Only if we could see the original rationale behind the preexisting choice.

Lambda methods Function(...) and Sub(...) don't need :, and require the "full-fat" implementation for more than one statement in body.

VBAndCs commented 5 years ago

@AdamSpeight2008 I think implements-methods should have bodies, because it will be too long line. I suggested once to make auto line break around Implements keyword, and Anthony Green agreed on the principle, just to break the too long head line, so it will make no since to add an extra line to this too long one! Lets make the role to allow bodiless methods only when they don't have implements.

DzonnyDZ commented 5 years ago

VB already allows lambda methods and functions like

Dim x As Action(Of Integer) = Sub(i) Console.WriteLine(i)

or

Dim x As Func(Of Integer, Integer) = Function(i) i + 7

so why not to allow this syntax also for member methods and functions like

Public Sub WriteIntToConsole(value As Integer) Console.WriteLine(value)

or

Public Function Plus7(value As Integer) As String (value + 7).ToString()

OK, this As String part looks little bit ugly, could be also

Public Function Plus7(value As Integer) (value + 7).ToString() As String

Or allow one of these syntaxes but as optional with type inference

Public Function Plus7(value As Integer) (value + 7).ToString()
zspitz commented 5 years ago

@DzonnyDZ Your proposed syntax is extremely difficult to read.

Happypig375 commented 5 years ago

What about

Public Function Plus7(value As Integer) As String = Function() (value + 7).ToString()
VBAndCs commented 5 years ago

I don't like lambda expressions in VB.NET at all!.. it is long, and confusing because the return value is not separated from the parameter list. I've suggested to use this syntax instead: Dim x = Fn(n) => n+1 Fn is a legacy VB keyword, and a famous shortcut for Function I like the => of C#, but if others dislike it, it can be replaced with : Dim x = Fn(n) : n+1 It will not be confused with the new line separator because the existence of the Fn keyword. Multiline lambdas should keep the Function keyword as is.

bandleader commented 5 years ago

@DzonnyDZ 's solution is only readable if we use type inference (same as we have with single-line lambdas). This is impossible as Roslyn does not support type inference for class-level methods.

Expression-bodied members are the best solution here, esp. since C# already has them. (Allowing a statement after a method declaration is OK as well, but presumably there's a reason they're not supported? In any case, might as well just do expression-bodied members; they're much nicer.)

gilfusion commented 4 years ago

Just thinking through some of the running questions...

(Warning, wall of text ahead.)

To :End Sub or Not

VB already has special behavior for : in single-line If statements.

If condition Then doThis() : doThat() Else doSomethingElse()

(If blocks follow the traditional behavior of :.)

If condition Then : doThis() : doThat() : Else : doSomethingElse() : End If

Can you use : to put a single-line If on the same line as another statement (opening the door to potential ambiguities)?

If condition Then : doThis() : If condition2 Then doThis() : doThat() Else doSomethingElse() : End If

VB already includes errors like BC32005 to cover some cases like this.

Okay, so how can this apply to single-line method declarations?

First, there is a precedent for single-line statements special-casing :, so @VBAndCs's suggestion for removing : End Sub sounds reasonable. (The reason I kept it in the original suggestion at the top was to keep this proposal as simple as possible, hoping that might make it more likely to happen.)

For a while, I was going to suggest making it optional, like the fact that you can use the single-line If or use :s to make the block If a single-line, too. If VB ever gets something like C#'s local functions, there could be an ambiguity issue (would : End Sub end a nested single-line Sub or the Sub it is contained in), but it looks like something like BC32005 might cover that case.

Still, of the three choices - making : End Sub required, forbidden, or optional - the optional choice seems the most complicated with not much benefit over the others. Meanwhile, the required and forbidden choices are mutually exclusive - one or the other would need to be chosen to move forward. I wonder if one of the reasons BC30040 was added in the first place was to leave room to choose either way in the future.

Right now, I would personally lean towards no End Sub if we can do without it - on a single-line declaration, it's just noise.

Expression or Statement Bodies?

Regarding making single-line Function and Get bodies expressions or statements: making them expressions saves you the Return keyword, but making them statements, especially with the single-line If precedent above, could let you : multiple statements into a single-line Function declaration. I could go either way (but, then again, it's not my decision to make).

Using A Completely Different Syntax?

There are, no doubt, many possible ways to get single-line declarations. I'll just leave that discussion over in #61 and keep focused on the : possibilities here. Ultimately, my hope is that we get there some day, no matter the syntax. VB's verbosity is both a blessing and a curse - in some ways, having things explicitly written out makes them clearer to understand, and in other cases, it's just noise and clutter in the code.

Closing: One More Potential Use Case

One place I have been wanting single-line method declarations a lot recently has been when writing method overloads. Often, one overload does all the work, and the others either insert omitted parameters or convert parameters from one type to another. For methods like that, three lines feels like overkill.

Public Sub Load(fileName As String)
    Load(New File(fileName))
End Sub

vs

Public Sub Load(fileName As String) : Load(New File(fileName))

especially when every method in a class that takes, say, a file object has an overload that takes a file name.

(Here's hoping these ramblings are coherent enough to be useful.)

paul1956 commented 4 years ago

To me the beauty of VB is that virtually programmer can read the code without knowing the meaning of strange symbols and cryptic syntax. If you want brief nothing is briefer then APL and there is a reason everyone is not programming it today. As an entry language clarity is more important then saving some typing. Omitting Optional is something I would support because the proposed syntax is very clear what the intent is, this proposal just makes code harder to understand. VS is great at helping align if/else/end if to make it clear what goes where, it look me a long time to figure out

If condition Then : doThis() : If condition2 Then doThis() : doThat() Else doSomethingElse() : End If
pricerc commented 4 years ago
Public Sub Load(fileName As String)
    Load(New File(fileName))
End Sub

vs

Public Sub Load(fileName As String) : Load(New File(fileName))

Ok, so this has come up before in this forum.

What does the developer save by using the one-line version?

As far as I can tell, nothing. You hit the ':' key instead of the Enter key.

But you do make it more complicated if you ever wanted to expand the method later on.

Looking at the OP, I'd argue that

Sub WriteFoo()
    Console.WriteLine("foo") : End Sub

should be illegal - End Sub should be required to be on its own line.

And I have to agree with @paul1956 - this is more likely to reduce VB's legibility, which is important to those of us who share code with new and non-programmers.

DzonnyDZ commented 4 years ago

I'd say the single line method • should be expression-bodied to save the return statement and because C# is • Not use : because single line if doesn't use it and C# doesn't need { • That way no need for End Sub

I think such syntax is clear, easy to understand and doesn't block future enhancements.

Can we infer the return type of inline function? :-)

If already : version would be allowed for sake of language completeness that one should require : End Sub

Ing. Jan Záruba dzonny.dz@gmail.com +420 728 886 739

Dne čt 16. 1. 2020 9:50 uživatel pricerc notifications@github.com napsal:

Public Sub Load(fileName As String) Load(New File(fileName))End Sub

vs

Public Sub Load(fileName As String) : Load(New File(fileName))

Ok, so this has come up before in this forum.

What does the developer save by using the one-line version?

As far as I can tell, nothing. You hit the ':' key instead of the Enter key.

But you do make it more complicated if you ever wanted to expand the method later on.

Looking at the OP, I'd argue that

Sub WriteFoo() Console.WriteLine("foo") : End Sub

should be illegal - End Sub should be required to be on its own line.

And I have to agree with @paul1956 https://github.com/paul1956 - this is more likely to reduce VB's legibility, which is important to those of us who share code with new and non-programmers.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dotnet/vblang/issues/477?email_source=notifications&email_token=AAQ6JS2N2Z3EGREJMRQGK73Q6ANWVA5CNFSM4I2ERR5KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJDITUI#issuecomment-575048145, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAQ6JS4NIVJZYIVWPFBRDXLQ6ANWVANCNFSM4I2ERR5A .

pricerc commented 4 years ago

Any time I see the phrase "because C#" in a discussion about VB features, I want to bang my head against my desk.

If you say "because C#", then (whether you intend to or not), you are saying that C# is a better language, and VB should be more like C#.

If you say "because C#", I say "bah humbug", and I read the rest of your argument through cynical eyes, because it's been tainted by "because C#"

VB is its own language, and changes should be for the benefit of VB programmers. Changes should offer some measurable improvement to the language in some way. It could be in improvements in productivity of the developers, or improving the stability, or adding support for a new .NET constructs (e.g. Span).

And any changes proposed should improve things without diluting the character of the language or making it harder to read and maintain afterwards.

Changes to VB should never be because "some other language has this cool thing, we should put it in VB so it can be cool too.".

Peace out.

pricerc commented 4 years ago

I think it is easy for people to forget that many seemingly verbose language constructs were designed intentionally by some very smart people.

As a result, millions of people love VB the way it is, extra words and everything.

It doesn't need to be C# with more words.

And the language doesn't need change for change's sake.

I'd rather energies were expended on improvements in the tooling, stability and performance of the .NET frameworks (all of them) than on new language features that aren't going to make me more productive or make the language easier to read.

just my 2c, as an old programmer just longing for some stability in his tools.

zspitz commented 4 years ago

@pricerc

many seemingly verbose language constructs were designed intentionally by some very smart people.

I'm still waiting for some possible justification for the misleading monstrosity that is TypeOf x Is y. Once pattern matching is introduced, perhaps this syntax can be deprecated once and for all.

paul1956 commented 4 years ago

If I was asked to prioritize VB feature it would be to first and foremost to be able to consume .NET libraries, block level options followed by allow unchecked math locally by that I mean

Unchecked Math On

image

Unchecked Math Off

The last two have been proposed #33 since 2017 if not earlier.

gilfusion commented 4 years ago

The reason I opened this issue in the first place is simply because every time I scroll through a class or module with a lot of one-liner methods (I tend to write a lot of convenience methods), I wonder if there isn't a way to make that code shorter (reduce excessive scrolling), in a way that fits in with what VB already has, in this case, the : statement separator.

Sub WriteFoo()
    Console.WriteLine("foo") : End Sub

is legal VB today, and

Sub Command1_Click(): Me.Print "Hello": End Sub

was legal in VB6 (dusted off a Working Model installer this morning to try it out - took a couple tries because I forgot that method calls sometimes didn't use parentheses back then), with something similar legal as far back as QBASIC.

Is it more or less readable? I suspect some aspects of readability are subjective, and I would have to see a bit more code to say with more certainty. (I probably wouldn't teach it to my high school students, mostly because they can barely keep up with one way to do things, let alone multiple ways to do the same thing.)

Is it a high priority? Probably not - just throwing ideas out there to see if they are low-hanging fruit.

pricerc commented 4 years ago

@pricerc

many seemingly verbose language constructs were designed intentionally by some very smart people.

I'm still waiting for some possible justification for the misleading monstrosity that is TypeOf x Is y. Once pattern matching is introduced, perhaps this syntax can be deprecated once and for all.

I said many not all :)

Without wanting to go off-topic, looking briefly at #277 again. One could argue that it looks a bit like the result of attempts to make it 'concise', instead of 'correct'.

pricerc commented 4 years ago

..., I wonder if there isn't a way to make that code shorter (reduce excessive scrolling), in a way that fits in with what VB already has, in this case, the : statement separator.

I have some sympathy with that, really I do. But.

Sub Command1_Click(): Me.Print "Hello": End Sub

was legal in VB6 ...

That may be, but I think that was a bad thing. That be a code smell.

The colon as a statement separator is as old as BASIC. But I don't think allowing multiple statements on a single line is helpful in the modern high-level OO language that is VB.NET.

And, as I've stated elsewhere in this forum, I think a single consistent pattern, even a subjectively verbose one, is (generally) preferable to having many alternatives to choose from. Long-term maintainability and legibility should take preference over short-term developer shortcuts.

Sub Command1_Click()
       Me.Print "Hello"
End Sub

Is a pattern that any VB developer instantly recognizes. It's one I've been using for over 20 years. I submit that your single line version would be trickier to spot when quickly browsing through a program listing.

I don't know about you, but if I'm scanning for a bit of code that does something, I'm not necessarily looking at the method names, I'm looking at what's in the methods. If I first have to scan and parse past the method declaration on a one-line method, that's adding work to my already-wishing-for-early-retirement brain. I suppose I've developed a kind of filter so that I don't see the 'noise' that is the method decorations, so I concede that this is subjective.

Let me come from a different angle. I'm currently doing some major updates on a C# project I delivered about 11 years ago. As part of that, I'm applying a bunch of refactoring recommendations from CodeRush. Among these recommendations are lots for converting properties and methods to expressions. And I've done a lot of it. But then, when I go back though the code while trying to remember what I was doing 11 years ago, I'm not finding it any easier to find things just because they're one-line express-bodies. If anything, it's harder, because I'm now looking for multiple different patterns instead of just one.

pricerc commented 4 years ago

If I was asked to prioritize VB feature it would be to first and foremost to be able to consume .NET libraries, block level options followed by allow unchecked math locally by that I mean

Unchecked Math On

image

Unchecked Math Off

The last two have been proposed #33 since 2017 if not earlier.

I'm 100% on consume .NET libraries and having an Unchecked Option (whatever form that takes).

I'm 50/50 on the block-level options. For: because sometimes you want different settings in the same class. Against: you can achieve the result with multiple files and partial classes, and in-code compiler directives are easily lost, having them at the top of the file may seem archaic, but it's reliable.

paul1956 commented 4 years ago

I always forget about partial classes, yes with that you would not need line level options, except for Unchecked. I prefer project level options except the the very few cases where one file needs something special. For Option Compare I specify it explicitly for string compares, and there is a Code Analyzer that is great at finding them. I am building an unchecked NuGet package for VB to handle Byte, SByte, Ushort, Short, UInteger, Integer, ULong and Long as an interim solution. I hope to post to GitHub in the next week or so if anyone want to comment.

DzonnyDZ commented 4 years ago

@pricerc I'm sorry for "because C#"-ing. I used that because C# implemented the feature before VB did. I won't say C# is better language than VB, it was just faster to implement particular feature. Speaking of that I can provide list of features VB has, C# doesn't have and it should have to make developers' live easier (starting with VB-style explicit interface implementation).

VBAndCs commented 4 years ago

I think this topic is over discussed. I advice to try answer dozens of questions Antony Green asked in Pattern-Based XML Literals #483 which can begin a new VB.NET era on WPF, Razor and Blazor (Server, Web and mobile bindings), and need a lot of team and community work. All small issues can be solved once VB.NET came back to the game. Thanks

pricerc commented 4 years ago

@DzonnyDZ

@pricerc I'm sorry for "because C#"-ing. I used that because C# implemented the feature before VB did. I won't say C# is better language than VB, it was just faster to implement particular feature. Speaking of that I can provide list of features VB has, C# doesn't have and it should have to make developers' live easier (starting with VB-style explicit interface implementation).

No need to apologise. We're all friends here (I hope). I am also just trying to be helpful.

I use both languages, so I'm aware of pros and cons of both. Because of the work I do (a lot of XML), I miss XML literals A LOT when working in C#.

But when proposing a new idea for VB, even if it's from something you've seen in another language, the proposal will look better if you frame it from a VB-only perspective. Explain how the thing will make VB better. Explain the problem you're trying to solve, and how your proposed solution addresses it. And do it without referencing another language (and that's not just for C#, other languages also get brought up occasionally). A good proposal should stand on its own without reference to other languages.

Once a general idea is accepted as a good one, referencing how other languages achieve the same result can be useful in the "how". But other languages should never be in the "Why".

AdamSpeight2008 commented 3 years ago

What about using :=

 Public Shared Operator <>(l As Source, r As source) As Boolean := l.ID <> r.ID

VB function have an implicitly defined variable that can use for the return value of function, that of it's method's name. Named Parameter Syntax utilises := to indicate what you are setting this explicitly named parameter to. So why not reuse that convention and allow simpler definition of "short" function / methods.