Closed RogueMacro closed 1 year ago
Yes, this is by design.
Foo<(String key, String value)>
is a different type than Foo<(String, String)>
. It's true that (String key, String value)
will implicitly cast to (String, String)
, but float
will implicitly cast to double
too, and they are definitely different types. (String key, String value)
will not, however, cast to (String zebra, String cat)
.
You could argue that Foo<(String key, String value)
should always considered to be "compatible" with Foo<(String, String)
, but remember that you could easily add a comptime initializer to Foo<T>
which uses the names of the tuple arguments to add new data members to Foo
, and then the memory layouts of those two types wouldn't even be the same...
But to be clear, Dictionary<String, String>
does not implement ICollection<(String, String)
, it implements ICollection<(String key, String value)
. "inherits from" is the wrong terminology there.
Yes, I understand. My actual use-case was this:
So I could easily just collect an enumerator into any collection without a for-loop. Worked around it by doing a map with tuple names:
Just to be clear again, there isn't a way to explicitly cast it from (String key, String value)
to (String, String)
(with generics)?
It is not (and cannot be) possible to provide a way to cast from Foo<(String key, String value)>
to Foo<(String, String)>
.
In your simple case, we know IEnumerable<(String key, String value)>
is REALLY the the "same thing" as Foo<(String, String)>
, but I could easily write a type using comptime where the internals of those two types are completely different.
I just don't see how the names of the tuple items matter in the memory layout? If no name is specified, 0, 1, ... and so on is used right? Or is it different to Item0
and such from C#?
The memory layout is not different. That's not the point.
The point is that if you have a Foo<T>
and a Foo<T2>
, those types can have different memory layouts even if T
and T2
have the same memory layout.
If you need an example:
class Foo<T>
{
public T mValue;
[OnCompile(.TypeInit), Comptime]
static void Init()
{
if (typeof(T) == typeof((String key, String value)))
Compiler.EmitTypeBody(typeof(Self), "int mExtraData;");
}
}
Thanks, I think I am dyslexic. I completely see your point. I just think it's a little stupid that named and unnamed tuples can't be considered the same type, just in case someone wants to create specific behavior if the tuple names match exactly.
Correct me if I'm wrong please, I'm trying to understand the reasoning behind this, but if (String key, String value)
were considered the same as (String, String)
, then you wouldn't be able to use comptime to change the memory layout anyway? Since you can't differentiate between the two? Is there any real use for what you showed in the example?
I can't see a scenario where it's useful to differentiate between (String zebra, String horse)
and (String key, String value)
to make changes to the structure.
Should we make struct Foo { float x; float y; }
also be the same thing as struct Bar { float dollars; float cents; }
? Or do member names only matter for structs but not tuples?
At first I would argue that Foo
and Bar
are explicitly differently named types, while tuples are anonymous. Just pure data structures? But I guess they can also represent different things like Foo
and Bar
represent different things.
Then again, why not create a struct if you want the tuple to represent something specific like a key-value pair. If you want the key-value pair tuple to not be mixed/confused with/as something other than a kvp, then shouldn't you instead create a KeyValuePair<...>
struct. That is in my opinion clearer, and almost exactly the same, right?.
I would think that since names for tuple items are optional, they are only there to help show what the value represents, not create a definite type.
It might be helpful for you to think of tuples more like anonymous types, then.
And when you omit field names then that just allows the compiler to name the fields for you and also you allows the compiler to create some implicit conversions operators to other some other tuple types.
IE: just read (float x, float y)
as an anonymous struct { float x; float y;}
that gets some special treatment for construction and type conversion.
If I understand correctly, the reason unnamed and named tuples cannot be "the same type" is that the field names say what the type represents, and shouldn't be changed to give it a different meaning?
If I can think of it as an anonymous struct, doesn't that also mean I get to decide what meaning to give it? In you example, I can say that x and y are positions on the screen, or they can be the size of an element. Isn't that's why it's anonymous? If I on the other hand say struct Size { float x; float y; }
, then it means specifically a size and not position.
If it is anonymous and not given a meaningful name, I think that I should be able to use them interchangeably. Sorry if I'm being stubborn, but my point is that (as in C#), if you want a (String key, String value)
to only be a key-value pair where it's used, why not do struct KeyValuePair<TKey, TValue> { TKey Key; TValue Value; }
.
You could also say that even if you had a struct Size { float x; float y; }
then it still lacks units so somewhere it might mean miles and somewhere else it might mean kilometers, but that disparity does not make the name Size
meaningless. Names still have meaning.
Anyway. I understand you have a different idea for how it should work but it's not changing.
Thanks, I feel more enlightened than I did an hour ago, even though my opinion stands. Perhaps I should make my own language where tuples can't have field names 🤔
I would very much suggest making your own language if you enjoy pain, you are looking for a project that can never ever be finished, you enjoy near-zero chances of "success", and you hate making money.
... but you get to call your language Beef🐮 and many people are grateful for your sacrifice ✝️
Oh, and you also get to deal with people telling you that your named your free project the wrong thing! So much fun.
Maybe you should consider charging for this service. Begin a monthly subscription you pay on time or else all your source code will be purged. Nothing better than a pay-to-win programming language!
Didn't know how to formulate this correctly in the title so here's what's happening. This throws an error even though it seems like perfectly valid code,
Dictionary<String, String>
do inherit fromICollection<(String, String)>
, but you have to specify the nameskey
andvalue
of the tuple:ICollection<(String key, String value)>
.