Tuples and ValueTuples

Viewed 346

Understanding how tuples works.

  1. What's the point of the tuple? Give pls one-several examples where tuples are really useful and better than other array-like types.
  2. Comparing
$t1 = [Tuple]::Create(4, 5)
$t2 = [Tuple]::Create(4, 5)
$t1 -eq $t2 #True (How??)

As i know, only this code can return "True":

$t1 = [ValueTuple]::Create(4, 5)
$t2 = [ValueTuple]::Create(4, 5)
$t1 -eq $t2 #True (This is correct)

That's the one of the differences between [Tuple] and [ValueTuple]. But...

  1. For what there is a possibility of creating empty tuple? How we can use this?
$t = [ValueTuple]::Create()
$t|fl        # Length : 0
$t.GetType() # ValueTuple

$t haven't .add() method, or smth like this.

  1. What is the syntax to create tuple in posh with named fields? Like(c#):
var tuple = (count: 5, sum: 10);
Console.WriteLine(tuple.count);
Console.WriteLine(tuple.GetType());
5
System.ValueTuple`2[System.Int32,System.Int32]
2 Answers
  1. To represent multiple values without having to declare an explicit type - especially useful inside methods (where an additional type would be pure overhead, and wouldn't add to expressivity); in the case of value-tuples, this is all done without any heap allocations (unlike arrays); also, arrays don't perform #2 below:
  2. Tuples perform memberwise equality tests
  3. (Edited) A zero-element value-tuple does exist (ValueTuple.Create()); this may be useful to create a nil value that implements IStructuralComparable, IStructuralEquatable, and ITuple - but this doesn't seem hugely useful, except to satisfy an API constraint
  4. You've... shown the syntax; this relates to value-tuples, and the name data is specific to the call-site: it doesn't actually exist anywhere in the type system (although it is exposed outwards to callers via attributes, it is not exposed inwards to callees - which means names can't be used by things like serialization tools or ORMs); essentially, it is just the compiler / parser - not the runtime - that knows about the names

Marc Gravell's helfpul answer covers the C# / general .NET perspective; let me add a PowerShell-specific one:

With respect to System.Tuple<...>, which are reference types:

$t1 -eq $t2 # True (How??)

PowerShell's -eq operator does not act like C's ==, which would test for reference equality here; put simply, PowerShell's -eq[ acts like $t1.Equals($t2), which with System.Tuple<...> (as well as with value type System.ValueTuple<...>) performs member-wise equality comparison and reports $true if all members are equal.

What is the syntax to create tuple in posh with named fields? Like(c#):

There is no such syntax in PowerShell - you'll have to use the default .Item<n> property (Tuple) / field (ValueTuple) names:

PS> ([ValueTuple]::Create(1, 'two')).Item2
two

However, PowerShell (Core) 7+ (not Windows PowerShell) offers convenient positional indexing:

PS> ([ValueTuple]::Create(1, 'two'))[1] # same as [-1] (last element) in this case 
two

In general, note that PowerShell's array literals are [object[]]-typed, so it's easy to create a collection of objects of disparate type:

PS> $arr = 1, 'two'; $arr[1]
two

If you want named elements, you can use a hashtable literal (which constructs a System.Collections.Hashtable with case-insensitive key lookup):

PS> $ht = @{ Id = 1; Name = 'two' }; $ht.Name
two

To create a "property-bag" ad-hoc object, use a [pscustomobject] "cast" (in reality, this is syntactic sugar for directly constructing an object of that type, i.e. a quasi-anonymous PowerShell-only object):

PS> $obj = [pscustomobject] @{ Id = 1; Name = 'two' }; $obj.Name
two

Note that all these options won't perform as well as ValueTuple, but performance isn't PowerShell's focus in general.


Therefore, from a PowerShell perspective the answer to:

What's the point of the tuple?

is:

Not much:

  • Using them for optimized performance is generally not worth it in a scripting language such as PowerShell.

  • Unlike in C#, there is no syntactic sugar for creating them, so explicit [ValueTuple]::Create(...) calls are always required, which are cumbersome.

  • PowerShell's array literals and hashtable literals, for indexed / named mixed-type data structures, as shown above - are convenient, PowerShell-idiomatic alternatives. Native PowerShell command do not use tuples (though you may need to use them for calling .NET APIs directly).

    • As an aside: The [pscustomobject] @{ foo = 1; bar = 2 } syntactic sugar for creating the PowerShell analog of anonymous types in C# isn't as concise as one would hope (compare to C#: new { foo = 1, bar = 2 }).
Related