vblang
vblang copied to clipboard
Deconstructors and discards?
Will VB.NET in any coming version, allow us to define and use deconstructors? Will it support discards?
The overridable sub Finalize
of Object
is actually the deconstructor, but it is invoked by GC.
@Berrysoft The deconstructor is different than the destructor. The deconstructor does the opposite function of the constructor, where you can assign many property values to many variables in one line of code. C# allowded to destruct tubles,and allowed to define void Destruct () inside structures and classes or as extention method to them, so you can destruct them as yuo do with tuples. Something like this: Dim std as new Student("Adam", 16) ' Constructor Dim (name string, age as Integer) = std ' Deconstructor
@MohammadHamdyGhanem Oh I'm sorry, I've made a mistake. I would like that feature, too. At least it should be implemented for tuples.
I wonder how discard could be implemented, underscore already has an use, I currently use double underscore for lamdas, but for fire&forget asyncs I have to declare a variable, like
Dim __ = SomeMethodAsync()
I know another possibility would be awaiting a Task.Run of an Async Sub, but vb compiler some times complains because some overloads can take Function instead of Sub and I don't like suppressing such warnings.
Anyone has an idea of which character could be used for Discard? :thinking:
@JustNrik This is the same in C#. They kept the ability to declare an _ named variable, beside using the _ discard symbol. The _ variable takes over, and if it appears in a block, all _ symbols in this block are treated as the _ variable. This ensures that old code doesn't break.
@JustNrik
I wonder how discard could be implemented, underscore already has an use...
How about [_]
? Or maybe just []
? It would look something like this:
Import System.Collections.Generic
Import System
Public Class Example
Public Shared Sub Main()
Dim ([], [], [], pop1, [], pop2) = QueryCityDataForYears("New York City", 1960, 2010)
Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}")
End Sub
Private Shared Function QueryCityDataForYears(name As String,
year1 As Integer, year2 As Integer) As (String, Double, Integer, Integer, Integer, Integer)
Dim population1 = 0, population2 = 0
Dim area = 0#
If name = "New York City" Then
area = 468.48
If year1 = 1960 Then
population1 = 7781984
End If
If year2 = 2010 Then
population2 = 8175133
End If
Return (name, area, year1, population1, year2, population2)
End If
Return ("", 0, 0, 0, 0, 0)
End Function
End Class
' The example displays the following output:
' Population change, 1960 to 2010: 393,149
@rskar-git Good idea! VB uses less []
than C-style language, and it won't lead to ambiguous.
@rskar-git How about extending the With
keyword to handle this case?
Change
Dim ([], [], [], pop1, [], pop2) = QueryCityDataForYears("New York City", 1960, 2010)
To
Dim (pop1:=.Item4, pop2:=.Item6) = With QueryCityDataForYears("New York City", 1960, 2010)
Or
With QueryCityDataForYears("New York City", 1960, 2010) Dim pop1:=.Item4, pop2:=.Item6
@rskar-git How about extending the
With
keyword to handle this case?Change
Dim ([], [], [], pop1, [], pop2) = QueryCityDataForYears("New York City", 1960, 2010)
To
Dim (pop1:=.Item4, pop2:=.Item6) = With QueryCityDataForYears("New York City", 1960, 2010)
Or
With QueryCityDataForYears("New York City", 1960, 2010) Dim pop1:=.Item4, pop2:=.Item6
hm and how is the compiler supposed to guess "popN"? or at least before the method is typed. The third option... I'm not sure, looks really weird...
Why even have a ~variable~ placeholder?
Dim ( , , , pop1, , pop2) = QueryCityDataForYears("New York City", 1960, 2010)
@reduckted
Why even have a variable placeholder?
Yes! This aligns quite naturally with VB.NET's syntax for calling optional parameters with later values filled in:
foo.Bar(, , , , "last argument")
C# doesn't allow passing parameters this way, which is why it needs a special placeholder character.
@reduckted Very nice. I think you should introduce this as a formal proposal, and you have my vote in advance. This also should include adding a deconstruct sub in xlasses and structures.
I like Dim ( , , , pop1, , pop2) = QueryCityDataForYears("New York City", 1960, 2010) or better Dim ( , , ,population1:= pop1, , pop2) = QueryCityDataForYears("New York City", 1960, 2010) with 15.5 you don't need to name every parameter.
Maybe we should allow foo(bar1, , , bar2, , bar3)
as well?
Maybe we should allow
foo(bar1, , , bar2, , bar3)
as well?
@Berrysoft In what context do you want this syntax? Standard function calls already allow this.
@zspitz I haven't heard of this, but thank you!
I wasn't completely convinced that my previous idea of leaving out the placeholder entirely is the best option, but after writing out the different ways that we could use deconstruction below, I think it's a pretty decent option.
These are my thoughts on what could be supported. I don't necessarily agree that all of these make sense to use, but I thought I'd list all the different ways that we could use deconstruction.
Using these types and functions:
Class Foo
Property Alpha As Integer
Property Beta As Integer
Property Gamma As Integer
End Class
Function GetFoo() As Foo
End Function
Function GetBar() As (Integer, Integer, Integer)
End Function
Function GetMeep() As (X As Integer, Y As Integer, Z As Integer)
End Function
We should/could be able to do these types of things (ignoring the exact syntax for now):
1. Declare variables and get the value of the Alpha and Beta properties.
Dim (alpha, beta) = GetFoo()
Note the case is different between the variable and the property name.
2. Get the value of the Alpha and Beta properties and store them in existing variables.
Dim alpha As Integer
Dim beta As Integer
(alpha, beta) = GetFoo()
3. Get the value of the Alpha and Beta properties and store them in variables of a different name.
Dim (a:=Alpha, b:=Beta) = GetFoo()
Debug.WriteLine(a) ' <-- `a` and `b` are the variables that were declared.
4. Create an anonymous type with specific properties.
Dim x = (New With {alpha, beta}) = GetFoo()
Debug.WriteLine(x.alpha) ' <-- The object has properties called `alpha` and `beta`.
5. Create an anonymous type with specific properties and specific names.
Dim x = (New With {.A = Alpha, .B = Beta}) = GetFoo()
Debug.WriteLine(x.A) ' <-- The object has properties called `A` and `B`.
6. Get the values out of a tuple.
Dim (x, y) = GetBar()
7. Get the values out of a tuple, skipping some of them.
Dim (, , z) = GetBar()
8. Get the values of a named tuple by name.
Dim (y, z) = GetMeep()
9. Get the values of a named tuple, renaming them.
Dim (yValue:=y, zValue:=z) = GetMeep()
Debug.WriteLine(yValue) ' <-- `yValue` and `zValue` are the variables that were declared.
10. Get the values of a named tuple, in a different order to the tuple.
Dim (z, x) = GetMeep()
Debug.WriteLine(x) ' <-- `x` received the value of the first element, not the second element in the tuple.
11. Deconstruct into properties or fields.
Public Property A As Integer
Public B As Integer
Sub DoStuff()
(A:=Alpha, B:=Beta) = GetFoo()
End Sub
Like I said, I don't necessarily agree that all of these make sense. Some of them seem to have a bit too much "magic" in them (matching the names of the variables with the names from a named tuple feels like it might be a step too far), but overall, a lot of these felt fairly natural when I was writing them.
@zspitz
Standard function calls already allow this.
This is true for optional params only. I suggest to extend it to include
Sub Foo(x As Integer, <Out> ByRef y As Integer, optional z As Integer)
End Sub
If we want to discard the result returned by Y, then we can write:
Foo(1, , 3)
and if we don't send z, then:
Foo(1)
C# already allows to discard out params. I vote for giving a new one-word identifier for the <Out> ByRef
such as OutRef
to make more sense.
@reduckted
4 and 5 maybe not very good. VB doesn't separate assignment =
and equality =
, thus a = b = c
usually means a = b == c
in C#.
3, 5, 9 and 11 make me confused, because the variable names are all before :=
, and I haven't seen such syntax.
10 may lead to ambiguous?
All else are perfect!
Yeah, 4 and 5 are kind of weird. If you already have an object or tuple, why create an anonymous object from it? Why not just use the object/tuple to begin with. I'm OK if those scenarios aren't supported.
10 could be ambiguous. I'd be more than happy to treat named and unnamed tuples the same way. That is, example 10 would have to be written as Dim (x, , z) = GetMeep()
.
For 3, 9 and 11 I went with :=
to keep it similar to what is used for property assignment on attributes, or using named parameters. That is, target:=value
. In this case, the variable is the target, and the member of the object/tuple is the value. That was my way of thinking anyway. 😄
I like := but agree with @reduckted that using := would be confusing in this context. 10 would drive me crazy and confuse any reader, I would read 10 as create new variables named z and x then assign the first element to z and second to x and discard the third. . This should be about decomposition which is very useful not renaming which I don't see a use for.
@MohammadHamdyGhanem today <Out> does nothing except allow the reader to know the intent of the function writer, hopefully soon it will suppress the message about using uninitialized variables and maybe someday in the future allow the variable to be declared as part of the function call as you can in C#.
Previous suggestions #278 and #292.
This is having more activity, so if it helps things I’d be happy to close #278.
PS: the lack of this confused a co-worker today who expected it to just work and ended up asking me what the correct syntax was for vb. Unfortunately I had to tell him it didn’t exist...
For the deconstructor, I suggest to declare it as a function without any params, and returns a tuple: sub Deconstruct() As (Integer, String) Return (Me.Id, Me.Name) End Sub This requires allowing overloaded functions that return different tuple signatures, becuse ambiguity can be resolved from the deconstruction syntax. I also suggest to allow us to use defualt deconstructor for each constructor in any class or structure We can mark our properties with some attribute i.e <Deconstructable(n)> where n is the oeder of this property in the deconstructed tuple. This wsy we don't have to write many deconstruction overloads or use many discards. If we deconstruct the object to a triple deconstructor, vb will return the properties marked as deconstructable with n = 1. 2 and 3. This can be done with reflection directly, or it can be done by auto defining some overloded deconstructors based on the marked properties while vb compiles ghe class.
Until tuple deconstruction is added, I use the following extension method (for a 2-tuple):
Function ForEach(Of T1, T2)(src As IEnumerable(Of (T1, T2)), action As Action(Of T1, T2))
For Each t In src
action(src.Item1, src.Item2)
Next
Return src
End Function
like this:
Dim seq = {"zero", "one", "two", "three", "four"}.Select(Function(x, index) (x, index))
seq.ForEach(Sub(x, index) Console.WriteLine($"{x} - {index}"))
Additional overloads can be added as needed.
It would be great to have something for function/sub parameter declarations. I'm a fan of the first [] idea or even the prior mentioned __ (double underscore) because both feel pretty natural. I need to be able to do something like…
Sub AFunc(ParamArray __())
DoStuff()
End Sub
…to match an expected signature in some horrible reflection-based logic. I would be willing to help implement it as a feature for vNext. I don't have much experience tinkering with roslyn though, so I'd appreciate it if someone could point me to whatever the correct branch to fork is and its associated contribution guidelines if they want me to do it.
@wessupermare you Fork Roslyn to your private GitHub repository, on your repository you click on green "Copy" box and copy the URL for example https://github.com/paul1956/roslyn.git. Open Visual Studio, in the startup window select Clone A Repository and after VS is finished, exit VS. Open a developer command prompt at ..Repos/Roslyn and then run .\Restore.CMD and .\Build.CMD. Now open Roslyn.sln to Compilers.sln and you are ready to start looking around. VB already has discards in the form of Function X(_0 as String) as String.
@paul1956 Thanks! I'll take a look. Good to know that someone already did it, you saved me a lot of time there.