Add exclusion/not type #17879
Replies: 0 comments 10 replies
-
Edit. After small researches about implementing this feature. I think it should become an infix operator like intersection and union: trait A
trait B extends A
trait C
//A ! B -> A without B.
//B ! A should throw an error because A is a superclass of B
//A ! C should be treated as A by the compiler (maybe a compile-time warning ?) Reasons of this change
GoalThe goal is to implement an operator equivalent to the exclusive operator from the set theory in mathematics. Here the proposed model uses the ExampleNote: Gray means "excluded" |
Beta Was this translation helpful? Give feedback.
-
See also the discussion on the contributors forum: https://contributors.scala-lang.org/t/union-types-and-generalisations/3820/9 |
Beta Was this translation helpful? Give feedback.
-
A couple of summary points from the thread that @LPTK mentioned (because it was a while ago!):
|
Beta Was this translation helpful? Give feedback.
-
Hello.
I think I understood but I would like to know how an example using your impl on 2.11 looks. |
Beta Was this translation helpful? Give feedback.
-
// this is only to verify the expected type.
import scala.reflect.runtime.universe.{TypeTag, typeTag}
trait SpookyFunction[-In, +Out] {
def andThen[NextIn <: In, NextOut](
next: SpookyFunction[Out with NextIn, NextOut]
): SpookyFunction[NextIn, Out with NextOut] =
new SpookyFunction[NextIn, Out with NextOut] {}
}
trait A; trait B; trait C; trait D; trait E
object Foo extends SpookyFunction[A with B, C]
object Bar extends SpookyFunction[C with D, E]
object Example extends App {
// to see what's inferred
def showType[T : TypeTag](t: T): Unit = println(typeTag[T])
// Expected: TypeTag[SpookyFunction[A with B with D,C with E]]
// Why? Because even though Bar requires C, it is provided by Foo and so it can be eliminated.
showType(Foo andThen Bar)
} This works in Scala 2.11, 2.12, 2.13, and 3. (The type inference part, not necessarily the syntax and |
Beta Was this translation helpful? Give feedback.
-
Note that it's very important in Scala 2.x to say: next: SpookyFunction[Out with NextIn, NextOut] rather than next: SpookyFunction[NextIn with Out, NextOut] (i.e. if you don't put the "thing to eliminate" first, it won't work.) Not sure if this caveat applies to Scala 3 or not. |
Beta Was this translation helpful? Give feedback.
-
Well it looks like we haven't read the same thread.
This was not well-agreed at all. A general negation type would in fact be a lot more tractable than smart matching in type inference. Currently, matching components in these types breaks in surprising ways if types are reordered (violating the commutativity property), because it's the problem of smartly matching unions and intersection during type inference that is intractable, not negation types. The potential soundness issue is more of a technicality in DOT as far as I can see. It's a safe initialization problem, and there are already many like it, so negation types don't meaningfully change anything here: #11716
Again, I think you have it backwards. Negation types do not any dramatic runtime implications; smarter type matching does, and it's necessary for a good library implementation of type subtraction. |
Beta Was this translation helpful? Give feedback.
-
@LPTK I agree with you that it would be useful (I suggested it on the thread, even). But a lot of folks smarter than me pointed out soundness issues with it. Apologies if I was mistaken about the level of consensus. |
Beta Was this translation helpful? Give feedback.
-
You say "a lot of folks" pointed out soundness issues, but only two people made concrete arguments regarding soundness issues AFAIK.
|
Beta Was this translation helpful? Give feedback.
-
The mentioned thread is a bit inactive so I will continue to write here. Icharan proposed a negation type x match {
case ~A => doStuff()
} This is readable but for the reasons talked above, I think an "exclusion type" should be more appropriate so I sent an example of pattern matching with this: x match {
case _ \ A => doStuff() //Or Any \ A
} This notation is the same like exclusion with Maths and it takes only one more character. I also find it more readable in more complex situations: trait A
trait B extends A
//Other child traits...
x match {
case A & ~B => doStuff()
} versus trait A
trait B extends A
//Other child traits...
x match {
case A \ B => doStuff()
} |
Beta Was this translation helpful? Give feedback.
-
Suggestion: add a way to exclude a type from an union. This could be caracterised by a "not" type to preserve the already used
-
.Examples:
A & !B
→ A and not B. This is equivalent toA - B
in mathematics.A | !B
→ A or not B. This is equivalent to*
!A | B
→ Not A or B. Equals to* - (A - B)
in mathematics. It forbids allA
exceptB
Beta Was this translation helpful? Give feedback.
All reactions