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
{{ message }}
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.
I think this is a known problem, but I did not find an issue currently documenting it.
Scala has these two important features:
path-dependent types
flow-based type refinement
The latter comes up in pattern matching (especially in the context of GADTs) and in boolean-based flow typing for explicit nulls.
The problem is that these two features do not play well with each other. Sometimes, we want flow-based refinement of a singleton's type in which path-dependent types may be rooted, and there is currently no way of achieving that.
Here is a simple and realistic example: I have a language definition interface called Base and a sub-language RunnableBase with more capabilities:
abstractclassBasewithtypeReptypeValue// other methods...abstractclassRunnableBaseextendsBasewithdefrun(r: Rep):Value
We should be able to retain path-dependent types and flow-refined types, so that the code below would type-check (it currently does not):
First, we have to use pattern c: (RunnableBase & b.type) instead of just c: RunnableBase (this is fine, just a little clunky);
Dotty does not reason that since value c matched value b, then c.Rep is the same as b.Rep; or alternatively, that b.type should be refined to be a subtype of RunnableBase in the corresponding pattern matching branch.
The problem does come up in the wild: Here I was working around the problem using a subtyping evidence between singleton types and a higher-kinded-type subst function (but the code was broken in 2.12.8). The underlying issue is that there is poor reasoning about refined singleton types.
GADT patmat cannot currently constrain singleton types. A mechanism for constraining singleton types is already implemented for explicit null types in their PR, so after that gets merged, we could look into reusing it for GADTs.
However, by now we know (see: scala/scala3#7882 (comment)) that the mechanism implemented for explicit nulls — which is to adapt variable references with a cast to the refined type — is insufficient because it loses the singletonness of the reference, and prevents it from being used in a path.
I think the best way to solve this issue would be to have a proper system for adding more information to singleton types, the same way more information is added to type variables in GADT pattern matching. Then, we'd need to:
refine singleton types using this mechanism when values of singleton types are pattern-matched and tested for null-ness; and
make sure to assign type c.type to a pattern variable b which matches a value of type c.type — this should not only work in the example above, but also in cases such as (_: Option[b.type]) match { case Some(c: T) => ... }.
The text was updated successfully, but these errors were encountered:
Reopening scala/scala3#7885 as a feature request, as suggested by @odersky.
I think this is a known problem, but I did not find an issue currently documenting it.
Scala has these two important features:
path-dependent types
flow-based type refinement
The latter comes up in pattern matching (especially in the context of GADTs) and in boolean-based flow typing for explicit nulls.
The problem is that these two features do not play well with each other. Sometimes, we want flow-based refinement of a singleton's type in which path-dependent types may be rooted, and there is currently no way of achieving that.
Here is a simple and realistic example: I have a language definition interface called
Base
and a sub-languageRunnableBase
with more capabilities:We should be able to retain path-dependent types and flow-refined types, so that the code below would type-check (it currently does not):
https://scastie.scala-lang.org/SozHLp3cSaODeLK1p9ohug
There are two problems here:
First, we have to use pattern
c: (RunnableBase & b.type)
instead of justc: RunnableBase
(this is fine, just a little clunky);Dotty does not reason that since value
c
matched valueb
, thenc.Rep
is the same asb.Rep
; or alternatively, thatb.type
should be refined to be a subtype ofRunnableBase
in the corresponding pattern matching branch.The problem does come up in the wild: Here I was working around the problem using a subtyping evidence between singleton types and a higher-kinded-type
subst
function (but the code was broken in 2.12.8). The underlying issue is that there is poor reasoning about refined singleton types.Another place where this would be useful: scala/scala3#7554 (comment), where @AleksanderBG commented:
However, by now we know (see: scala/scala3#7882 (comment)) that the mechanism implemented for explicit nulls — which is to adapt variable references with a cast to the refined type — is insufficient because it loses the singletonness of the reference, and prevents it from being used in a path.
I think the best way to solve this issue would be to have a proper system for adding more information to singleton types, the same way more information is added to type variables in GADT pattern matching. Then, we'd need to:
refine singleton types using this mechanism when values of singleton types are pattern-matched and tested for null-ness; and
make sure to assign type
c.type
to a pattern variableb
which matches a value of typec.type
— this should not only work in the example above, but also in cases such as(_: Option[b.type]) match { case Some(c: T) => ... }
.The text was updated successfully, but these errors were encountered: