Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The .NET Tuple types Tuple<T1,…> and ValueTuple<T1,…> are not sequences — though the ITuple interface does enable item access by index without reflection — and ValueTuple<T1,…> instances are mutable, but .NET actually has two different immutable sequence types, ImmutableArray<T> and ImmutableList<T>, which are functionally similar but have different performance characteristics[1].

Along with the rest of the .NET immutable collection types[2], both are persistent data structures in the sense noted above.

In contrast, .NET tuple types are, as you say, lightweight, anonymous structures addressed by field. The ValueTuple<T1,…> types, in particular, are used in the underlying implementation of the C# tuple language feature[3].

AFAIK, Python has no built-in anonymous mutable structure types, though since type names in Python are basically only used for display purposes, you can easily create them at runtime, e.g.,

    def anon(**kwargs):
        class _:
            __slots__ = tuple(kwargs.keys())
    
            def __repr__(self):
                return 'anon(' \
                    + ', '.join((f'{i}={getattr(self, i)!r}' \
                                 for i in self.__slots__)) \
                    + ')'
    
            def __eq__(self, other):
                if not hasattr(other, '__slots__') \
                   or sorted(self.__slots__) \
                   != sorted(other.__slots__):
                    return False
                for i in self.__slots__:
                    if getattr(self, i) != getattr(other, i):
                        return False
                return True
            
        o = _()
        for k,v in kwargs.items():
            setattr(o, k, v)
        return o
    
Note that this differs in two notable ways from C# tuples:

1. Each object created has a unique type, so

    type(anon(x=1, y=2)) != type(anon(x=1, y=2))
More importantly, this means a distinct type object is created and stored for every call to anon, which could have significant performance implications at scale.

This could be easily fixed with a cache of already-created anonymous types (trading off slightly increased object-creation time, of course).

2. Equality in the above implementation is based on the equality of identically-named values rather than identically positioned values; in C#, we have

    (x: 1, y: 2) != (y: 2, x: 1)
and

    (x: 1, y: 2) == (y: 1, x: 2),
while in my implementation,

    anon(x=1, y=2) == anon(y=2, x=1)
and

    anon(x=1, y=2) != anon(y=1, x=2).
This was by choice, as it seems more intuitive; C# behavior is no more difficult to implement.

For read-only anonymous structure types, the Python standard library has namedtuple[4] (which, incidentally, bases value equality on position, not attribute name, so C#'s behavior is arguably more "Pythonic" than my own).

[1] https://learn.microsoft.com/en-us/dotnet/api/system.collecti...

[2] https://learn.microsoft.com/en-us/dotnet/api/system.collecti...

[3] https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...

[4] https://docs.python.org/3/library/collections.html#collectio...



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: