You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The list returned by .typeMembers is ordered by declaration order, with type parameters first and type members second. BUT only if the total number of type members + type parameters is less than 5. Otherwise the order is non-deterministic due to underlying usage of Set.
I think expecting a defined order in Symbold.typeMembers method is reasonable, because:
When this method was added, the tests added with it only tested naive usage .typeMembers.filter(_.isTypeParam) – indicating that this is the correct usage pattern.
It's the only source of type parameter variance in public reflection API.
Restoring order without relying on it being correct in .typeMembers is very hard - the only way I found is to match type parameter names with the same from .primaryConstructor - and that's hardly intuitive and I'm not even sure if all types that can have variance defined on type parameters also have a primaryConstructor, otherwise the order is not restorable.
The .typeMembers call returns a List, not a Set, indicating order.
A user of izumi-reflect uncovered this undefined behavior due to getting incorrect variance in typetags generated by izumi-reflect on classes with many type parameters: zio/izumi-reflect#511
I'm not sure there's a bulletproof workaround for this issue downstream – because I'm not sure if every relevant type that may be inspected by izumi-reflect has a .primaryConstructor to match order against.
The text was updated successfully, but these errors were encountered:
What's interesting is that it's non-deterministic across Scala versions...
scala-cli run . -S 3.6.3
# Compiling project (Scala 3.6.3, JVM (23))# Compiled project (Scala 3.6.3, JVM (23))# List(type A, type B, type C, type D)# List(type A, type E, type D, type B, type C)
scala-cli run . -S 3.3.4
# Compiling project (Scala 3.3.4, JVM (23))# Compiled project (Scala 3.3.4, JVM (23))# List(type A, type B, type C, type D)# List(type A, type D, type C, type E, type B)
scala-cli run . -S 3.3.5
# Compiling project (Scala 3.3.5, JVM (23))# Compiled project (Scala 3.3.5, JVM (23))# List(type A, type B, type C, type D)# List(type A, type C, type E, type D, type B)
scala-cli run . -S 3.nightly
# Compiling project (Scala 3.7.0-RC1-bin-20250129-81e057a-NIGHTLY, JVM (23))# Compiled project (Scala 3.7.0-RC1-bin-20250129-81e057a-NIGHTLY, JVM (23))# List(type A, type B, type C, type D)# List(type B, type D, type A, type C, type E)
Indeed, List instead of Set might suggest relevance of order, but changing the method signature to Set is not an option at this point.
But for this particular use case .declaredTypes.filter(_.isTypeParam) seems to return actual type parameters in the expected order, even for bigger arities. If you also need information about type parameters of supertypes, then you'll have to traverse the inheritance tree explicitly by yourself and handle the parameters of the supertypes explicitly, because variance of a type parameter might slightly vary between the sub- and the supertype, e.g.
but changing the method signature to Set is not an option at this point.
This is misleading and, at the very least, should be documented with a docstring. I think the underlying collection should either be changed to an ordered set or the signature updated.
Compiler version
3.3.4
Minimized code
Output
Expectation
The list returned by
.typeMembers
is ordered by declaration order, with type parameters first and type members second. BUT only if the total number of type members + type parameters is less than 5. Otherwise the order is non-deterministic due to underlying usage ofSet
.I think expecting a defined order in
Symbold.typeMembers
method is reasonable, because:.typeMembers.filter(_.isTypeParam)
– indicating that this is the correct usage pattern..typeMembers
is very hard - the only way I found is to match type parameter names with the same from.primaryConstructor
- and that's hardly intuitive and I'm not even sure if all types that can have variance defined on type parameters also have a primaryConstructor, otherwise the order is not restorable.List
, not aSet
, indicating order.A user of
izumi-reflect
uncovered this undefined behavior due to getting incorrect variance in typetags generated by izumi-reflect on classes with many type parameters: zio/izumi-reflect#511I'm not sure there's a bulletproof workaround for this issue downstream – because I'm not sure if every relevant type that may be inspected by izumi-reflect has a .primaryConstructor to match order against.
The text was updated successfully, but these errors were encountered: