-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Projection type paths can't traverse singleton types #18655
Comments
@sjrd asking for opinion on subtyping theory here |
I believe the behavior of the compiler is correct. Remember that type projections in Scala 3 are not nearly as powerful as in Scala 2. In On the other hand, we have path-dependent types Note that |
Hi! Thank you for your reply! You know the intricacies of Scala's type system lots better than I do, but as a user, this makes very little sense. A Scala Projection types were made more cautious in Scala 3, requiring resolvability to concrete type, which is great, because it was possible to show circumventions of soundness when resolution to abstract types was allowed. But do there exist any circumstances where ...and revise how I structure programs! Very often, I tacitly assume in Scala a close equivalence between, say object A:
object B:
case class Foo(x : Int)
case class Bar(y : String) and object A:
case class B_Foo(x:Int)
case class B_Bar(y:String) That is, I use object nesting to reify namespaces and keep code ordered and organized. This projection-type issue is the first time I've encountered a discrimination that renders informal prefixing safer and more powerful than the nesting, which I usually consider preferable. Here Again, I have to apologize (and express gratitude for all of your work!) that I don't understand the intricacies of the type system that you refer to. But I don't understand: If Thank you again for your detailed response! |
An
The issue with that line of reasoning is: that we have not found a problem yet does not mean that there cannot be any problem. We have to prove, or at least convince ourselves, that no possible usages of the pattern can lead to a problem. In this case, we don't really know what
With nested
The latter is not valid either. It's just that we tolerate keeping |
Yes! But all I want is for concrete type
I have tremendous admiration for what you do, and I wish I were capable of helping on this score. My intuition is that treating concrete type Of course I understand that you cannot rely upon my intuition. Intuitions of people much more sophisticated about type soundness than I am have proven mistaken before. I guess I'd ask, though, that until you have pretty good reason to believe the intuition is wrong that you not affirmatively declare the compiler's current behavior to be correct. Addressing this may be hard within the compiler, and given the relative obscurity of projection types generally, I understand modifying the compiler to distinguish between more and less stable projection type paths might not be a high priority. But the intuition that concrete types (This did actually come up in a real project, where I now use a workaround suggested by @aly from the scala-users discord.) Thank you again for your help and your work! |
Overall, I agree with you that The main culprit here is not I recommend you think not about using flat names instead of |
This is getting a bit off-topic, but I find that when I do use true instance-dependent types (as opposed to types just "dependent" on a nested hierarchy of singleton objects), I usually do find myself needing to resort to projection types for some abstractions. I try to stick with traditional independent types unless there's a good reason, because in my experience somehow more complexity than I expect results from instance-dependent types. But sometimes there is good reason. In this case, I have an API made of lots of little elements ( To avoid projection types, I initially defined abstract versions of each of the elements without their typed payloads, and wrote reporters in terms of that. But as the API evolved, it was annoying to evolve the inheritance hierarchy with it, and I'd sometimes be surprised by variance issues. I suspect I could have powered through all that. But it seemed (and still seems) a bit dumb, when projection types are abstractions that represent precisely what I want without any extra code to maintain. The projection type captures everything that is common across different deployments of elements that doesn't depend on their shared type parameter. All this is to say that I know projection types are kind of an afterthought and downplayed. But to the degree that instance-dependent types are an abstraction worth their complexity, I think that projection types are as well. This ability to specify everything-that-is-common about large families of related objects without requiring explicit specification of that commonality with inheritance is a very cool trick! It is a bit unfair of me to say this, because it is not my work to take on but yours, but I think that continuing to explore and perfect projection types is worth the trouble, even though we've seen that they are not always trivial to reason about. Again, easy for me to say. Please don't think I don't appreciate your work. Scala is just an extraordinary achievement, and building and perfecting an ever safer, ever more expressive type system is really a kind of heroism. Thank you again! |
Compiler version
3.3.1
Minimized code (scala-cli scipts)
I'd like to have a type that expresses any
Outer
'sFoo.Data
. Two logical approaches fail.I thought that the issue might only be a parser issue with
.
in the type projection paths, so tried a "bridge" workaround. The type alias declaration succeeds! Unfortunately, the compiler is unable to prove that the expected projections conform.Expectation
I expect that I'd be able to write a projection type that would abstract over the dependent singleton type
Foo
. (In real life, I just want to write some functions accepting nested dependent classes and generate reports that would not depend on the identity of the enclosing instance.)As shown, with the type-alias bridge, I can successfully cast to a projection type and work without. But the compiler can't recognize the conformity of the type without the cast.
Thanks!
Thanks to Alexandru Nedelcu, Aly, and SlowBrain on the Scala discord for helping me work through this.
The text was updated successfully, but these errors were encountered: