dotnet / vblang

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

Proposal: Removing limitations of Tuples, by adding reference tuples and generating custom tuple types #511

Open vbcodec opened 7 years ago

vbcodec commented 7 years ago

Reference tuple types I have been using tuples with Preview 5, and lack of reference types is particularly striking. With ValueTuple as structure, I can't use tuples for mutable lists or dictionaries.

Dim l = new List(Of (X As Integer, Y As Integer)
l.Add((1,1))
l(0).X = 2 ' compiler error, member of structure can't be modified

Replacing whole tuple

l(0) = (2, l(0).Y)

is very unpractical, and nobody will update lists that way, especially with higher number of tuple's members.

For now I have solved this problem by creating my own library with tuples identical to those in your library posted on nuget, except that ValueTuple types are classes instead of structures. After referencing this library to the app, tuples in lists can be mutable.

I suggest to add another set of tuple types to the library that you created and posted on nuget. New tuple types will be classes with another name (for example ReferenceTuple). To specify that reference tuples must be used, there must be added entry in AssemblyInfo,

<Assembly: TupleReferenceTypes(True)>

(BTW: If external library with tuples is not referenced by app, then compiler error says that 'ValueTuple must be defined or imported'. How to define these ValueTuple in my app code, if I do not want to use external library ? Docs do not state how to do it. )

Custom generated types for tuples. Tuples using ValueTuple are not suitable for reflection scenarios (databinding, serialization and others), because these scenarios require original names of members. These limitations can be removed, when tuples will be handled by compiler generated custom types, instead of ValueTuple types.

Custom types for tuples are enhancement, not replacement, so user may decide which model is used across assembly / app, by adding entry to AssemblyInfo

<Assembly: TupleCustomTypes(True)>

There is need that names of compiler generated types must be deterministic, because serialization requires that behaviour to operate correctly. For example for tuple:

Dim t = (A:=0, B:=(C:="abc", D:=0))

name may be like VT_A_INT_B_TUPLE2_C_STRING_DINT (prefix VT means value type, while RT_ means reference type, if TupleReferenceTypes(True) is specified for app/library). Such name is too long, so may be hashed to shorter variant, and final name for custom type is Tuple_7654567. Such generated names are bit cryptic, but they are only used to carry data, and with widespread use of type inference, this won't be problematic.

These custom types must be unique across assemblies,and this may be achieved in some special way. Custom types won't be included into app/library, but into external assembly with fixed name (for example TupleCustomTypes.dll). This external assembly will be automatically created and updated by compiler. App/library will get only metadata which describe custom types that are used internally, and these types will be referenced from external assembly (TupleCustomTypes.dll). When some library with metadata will be referenced by project, then compiler will read these metadata, and update external assembly (TupleCustomTypes.dll). For backward compatibility with previous compilers, external assembly must be shipped with library. There also may be created external tool, that generate single external assembly, from multiple libraries with metadata.

These two enhancements, will make tuples really shining and universally accessible for much many scenarios, than currently.

yume-chan commented 7 years ago
  1. When designing the tuple literal feature, it requires the ValueTuple type to be a structure.
  2. There is a reference type called Tuple far before ValueTuple is invented, However, as a structure, ValueTuple can decrease allocations to provide performance and memory usage benefits in most cases, so ValueTuple is chosen to work with tuple literal.
  3. ValueTuple.dll contains a TupleExtensions class that provides deconstruction for Tuple and conversation between Tuple and ValueTuple. You can use (1, 2).ToTuple() to convert it to a Tuple, a reference type, to fit your requirement. But as you expected, the item names are lost.
  4. Compiler can't generate anonymous structures for every usage because Tuple represents some ordered values, but the item names, are not important. So if there is a method needs a (int a, int b) parameter, you can use a (int c, int d) as an argument, or even (int b, int a), or just (int, int). They are all compatible. Auto-generated anonymous structures will need conversations here, causes an impact to the performance, your complex reference merge way also doesn't help this.
DavidArno commented 7 years ago

I have been using tuples with Preview 5, and lack of reference types is particularly striking. With ValueTuple as structure, I can't use tuples for mutable lists or dictionaries.

Tuples are intended to be used as lightweight immutable values. If you want collections of mutable data, create classes; don't use tuples.

vbcodec commented 7 years ago

@CnSimonChan

it requires the ValueTuple type to be a structure.

ValueTuple was chosen for performance only. Tuple syntax is independent from data vehicle, so it do not requires structures,

conversation between Tuple and ValueTuple

For me, System.Tuples are useless, as they can't contain original names, are immutable, requires lot of code to define and initialize.

Compiler can't generate anonymous structures for every usage because Tuple represents some ordered values, but the item names, are not important

Custom types are enhancement, not replacement, as I have stated above. For tuples without names (like (int b, int a), or (int, int)), compiler may not generate custom types and use ValueTuple or ReferenceTuple, depending on TupleReferenceTypes switch.

@DavidArno Tuples don't have to be lightweight, and be defined as complex 'types' (with large number of variables and tuples inside tuples, etc). Tuple was created mainly to wipe out usage of temporary classes/structures, small arrays and inefficient System.Tuple. With tuples code is just shorter and cleaner, than without them, while performance is the same. Current design with ValueTuples only, was chosen because is simplest and fastest to implement. But developers, do not care what is fast to implement, but what they can do with given feature. And they will be disappointed if they discover that 'looking great' tuples cannot be mutable and bindable.

Typical and very common scenario with LOB (and other) apps where tuples have high potential to be used, is code like:

    Public Function GetCustomers As List(Of (ID As Integer, Name As String))
        ....
    End Function

    Dim customers = GetCustomers()
    AddInfoToNames(customers)
    CustCombi.DataSource = customers

, but that potential must be implemented.

DavidArno commented 7 years ago

@vbcodec,

Tuples don't have to be lightweight, and be defined as complex 'types' (with large number of variables and tuples inside tuples, etc).

Sure, that is the nature of code. It doesn't have to be written well.

Tuple was created mainly to wipe out usage of temporary classes/structures, small arrays and inefficient System.Tuple.

No. They were created to allow the expression of compound values in maths. Their use in OO languages is a fairly modern thing.

Current design with ValueTuples only, was chosen because is simplest and fastest to implement.

Nope, it was chosen because, in scenarios where one is creating and destroying many immutable tuples, using structs avoids hitting the heap and the associated garbage collection delays. .NET Tuples types were already immutable classes; structs just work more efficiently for such immutable data types.

But developers, do not care what is fast to implement, but what they can do with given feature. And they will be disappointed if they discover that 'looking great' tuples cannot be mutable and bindable.

Some developers will be disappointed; sure. So what? They are are "bad old, mutable, days" developers.

Typical and very common scenario with LOB (and other) apps where tuples have high potential to be used, is code like:

Public Function GetCustomers As List(Of (ID As Integer, Name As String))
    ....
End Function

Dim customers = GetCustomers()
AddInfoToNames(customers)
CustCombi.DataSource = customers

, but that potential must be implemented.

Just change:

AddInfoToNames(customers)
CustCombi.DataSource = customers

To:

Dim customersWithNames = AddNamesToCustomers(customers)
CustCombi.DataSource = customersWithNames 

and all will be fine. Stop living in the bad old days and start thinking like a modern "pit of success", immutable-by-default developer.

zspitz commented 4 years ago

If you really need custom types and value-tuples-as-reference-types, why not use an anonymous type? The only drawback AFAICT is that anonymous types don't have an officially supported ordering of the elements.

RevensofT commented 4 years ago

Umm.... sir, it's NOTHING TO DO with value tuple, it's NATURAL of STRUCTURE type, you can try it with any structure with a field member and all of them can't set field value unless you store it at local variant.

PS. So where value tuple is so shining ? It's the place call Pure functional programming.

Function Factorial(Input As Int32) As Int32
      Return (I:=Input, O:=2).do( 
                  [Until]:=Function(Arg) Arg.I < 3, 
                  [Do]:=Function(Arg) (Arg.I - 1, Arg.O * Arg.I)
                  ).O
End Function
VBAndCs commented 4 years ago

It can be solved by this proposal

  l(0).X <= 2
RevensofT commented 4 years ago

Wait ! Oct 7, 2016 !? did sir gafter change job to necromancer ? XD

Happypig375 commented 4 years ago

@RevensofT It's called an issue transfer.

@gafter gafter transferred this issue from dotnet/roslyn 4 days ago @agocke agocke transferred this issue from dotnet/csharplang 21 hours ago

RevensofT commented 4 years ago

@Happypig375 I know, I just can't help myself to joke about it to relax. :)

dovisutu commented 4 years ago

agocke transferred this issue from dotnet/csharplang 21 hours ago

I think what's interesting is that this somehow get transferred into csharplang...

I must say that this problem is quite usual and sometimes annoying... Looking forward to this getting approved.