1+ package progscala3 .typesystem .intersectionunion
2+
3+ object IntersectionUnion :
4+
5+ trait M :
6+ def m (s : String ): String = s
7+ trait T1 extends M :
8+ override def m (s : String ): String = s " [ ${super .m(s)} ] "
9+ trait T2 extends M :
10+ override def m (s : String ): String = s " ( ${super .m(s)} ) "
11+ trait T3 extends M :
12+ override def m (s : String ): String = s " | ${super .m(s)} | "
13+ open class C extends M :
14+ override def m (s : String ): String = s " { ${super .m(s)} } "
15+
16+ val c123 = new C with T1 with T2 with T3
17+ val c321 = new C with T3 with T2 with T1
18+
19+ def checkM (): Unit =
20+ val m : M = new C
21+ assert(m.m(" hello" ) == " { hello }" , m.m(" hello" ))
22+
23+ assert(c123.m(" hello" ) == " | ( [ { hello } ] ) |" )
24+ assert(c321.m(" hello" ) == " [ ( | { hello } | ) ]" )
25+
26+ def checkIntersectionCommutativity (): Unit =
27+ val ct1t2t3_c123 : C & T1 & T2 & T3 = c123
28+ val t1ct2t3_c123 : T1 & C & T2 & T3 = c123
29+ val t1t2ct3_c123 : T1 & T2 & C & T3 = c123
30+ val t1t2t3c_c123 : T1 & T2 & T3 & C = c123
31+
32+ val ct3t2t1_c123 : C & T3 & T2 & T1 = c123
33+ val t3ct2t1_c123 : T3 & C & T2 & T1 = c123
34+ val t3t2ct1_c123 : T3 & T2 & C & T1 = c123
35+ val t3t2t1c_c123 : T3 & T2 & T1 & C = c123
36+
37+ val ct1t2t3_c321 : C & T1 & T2 & T3 = c321
38+ val t1ct2t3_c321 : T1 & C & T2 & T3 = c321
39+ val t1t2ct3_c321 : T1 & T2 & C & T3 = c321
40+ val t1t2t3c_c321 : T1 & T2 & T3 & C = c321
41+
42+ val ct3t2t1_c321 : C & T3 & T2 & T1 = c321
43+ val t3ct2t1_c321 : T3 & C & T2 & T1 = c321
44+ val t3t2ct1_c321 : T3 & T2 & C & T1 = c321
45+ val t3t2t1c_c321 : T3 & T2 & T1 & C = c321
46+
47+ def checkIntersectionSubtyping (): Unit =
48+ val t1a : T1 = c123
49+ val t2a : T2 = c123
50+ val t3a : T3 = c123
51+ val c2a : C = c123
52+
53+ val t123 : T1 & T2 & T3 = c123
54+ val ct1 : C & T1 = c123
55+ val ct2 : C & T2 = c123
56+ val ct3 : C & T3 = c123
57+
58+ def checkIntersectionFunctionUsage (): Unit =
59+ def f (t123 : T1 & T2 & T3 ): String = t123.m(" hello!" )
60+ val list123 : Seq [T1 & T2 & T3 ] = Seq (c123, c321)
61+ assert(list123.map(f) == List (" | ( [ { hello! } ] ) |" , " [ ( | { hello! } | ) ]" ))
62+
63+ def checkIntersectionCovariance (): Unit =
64+ val listt1t2t3 : Seq [T1 & T2 & T3 ] = Seq (c123, c321)
65+ val list1 : Seq [T1 ] = listt1t2t3
66+ val list2 : Seq [T2 ] = listt1t2t3
67+ val list3 : Seq [T3 ] = listt1t2t3
68+ val list123 : Seq [T1 ] & Seq [T2 ] & Seq [T3 ] = listt1t2t3
69+
70+ // f(list1.head) // ERROR: "Found T1, Required T1 & T2 & T3"
71+ // f(list2.head) // ERROR: "Found T2, Required T1 & T2 & T3"
72+
73+ case class Bad (message : String )
74+ case class Good (i : Int )
75+
76+ def checkUnionGoodBad (): Unit =
77+ val error = Bad (" Failed!" )
78+ val result = Good (0 )
79+
80+ val seq1 = Seq (error, result) // Inferred type: Seq[T1nyRef] or Seq[Object]!
81+ val seq : Seq [Good | Bad ] = Seq (error, result)
82+
83+ def work (i : Int ): Good | Bad =
84+ if i > 0 then Bad (s " $i must be <= 0 " ) else Good (i)
85+
86+ def process (result : Good | Bad ): String = result match
87+ case Bad (message) => message
88+ case Good (value) => s " Success! value = $value"
89+
90+ val results = Seq (0 , 1 ).map(work)
91+ val strings = results.map(process)
92+ println(s " results = ${results.mkString(" , " )}, strings = ${strings.mkString(" , " )}" )
93+
94+ def checkUnionLaws (): Unit =
95+ summon[(T1 & (T2 | T3 )) =:= ((T1 & T2 ) | (T1 & T3 ))]
96+ summon[(T1 | (T2 & T3 )) =:= ((T1 | T2 ) & (T1 | T3 ))]
97+
98+ val x1 : T1 & (T2 | T3 ) = new T1 with T2 {}
99+ val x2 : T1 & (T2 | T3 ) = new T1 with T3 {}
100+ val x3 : T1 & (T2 | T3 ) = new T1 with T2 with T3 {}
101+ val x4 : (T1 & T2 ) | (T1 & T3 ) = new T1 with T2 {}
102+ val x5 : (T1 & T2 ) | (T1 & T3 ) = new T1 with T3 {}
103+ val x6 : (T1 & T2 ) | (T1 & T3 ) = new T1 with T2 with T3 {}
104+
105+ val x7 : T1 | (T2 & T3 ) = new T1 {}
106+ val x8 : T1 | (T2 & T3 ) = new T2 with T3 {}
107+ val x9 : T1 | (T2 & T3 ) = new T1 with T2 with T3 {}
108+ val x10 : (T1 | T2 ) & (T1 | T3 ) = new T1 {}
109+ val x11 : (T1 | T2 ) & (T1 | T3 ) = new T2 with T3 {}
110+ val x12 : (T1 | T2 ) & (T1 | T3 ) = new T1 with T2 with T3 {}
111+
112+ def checkUnionCovariance (): Unit =
113+ val seqT1s : Seq [T1 ] = Seq (new T1 {})
114+ val seqT2s : Seq [T2 ] = Seq (new T2 {})
115+ val seqT3s : Seq [T3 ] = Seq (new T3 {})
116+ val seqT1T2T3s1 : Seq [T1 | T2 | T3 ] = seqT1s
117+ val seqT1T2T3s2 : Seq [T1 | T2 | T3 ] = seqT2s
118+ val seqT1T2T3s3 : Seq [T1 | T2 | T3 ] = seqT3s
119+
120+ val tT1T2T3s : Seq [T1 | T2 | T3 ] = Seq (new T1 {}, new T2 {}, new T3 {})
121+ // val tT1s: Seq[T1] = tT1T2T3s // ERROR
122+ // val tT2s: Seq[T2] = tT1T2T3s // ERROR
123+ // val tT3s: Seq[T3] = tT1T2T3s // ERROR
124+
125+ def checkUnionContravariantFunctions (): Unit =
126+ val fT1T2T31 : (T1 | T2 | T3 ) => String = _ match
127+ case t1 : T1 => " T1"
128+ case t2 : T2 => " T2"
129+ case t3 : T3 => " T3"
130+ val fT1T2T32 : (T1 => String ) & (T2 => String ) & (T3 => String ) = fT1T2T31
131+
132+ val seqT1T2T3s : Seq [T1 | T2 | T3 ] = Seq (new T1 {}, new T2 {}, new T3 {})
133+ seqT1T2T3s.map(fT1T2T31)
134+ seqT1T2T3s.map(fT1T2T32)
135+ seqT1T2T3s.map((x : AnyRef ) => s " < $x> " )
136+
137+ def main (args : Array [String ]): Unit =
138+ checkM()
139+ checkIntersectionCommutativity()
140+ checkIntersectionSubtyping()
141+ checkIntersectionFunctionUsage()
142+ checkIntersectionCovariance()
143+ checkUnionGoodBad()
144+ checkUnionLaws()
145+ checkUnionCovariance()
146+ checkUnionContravariantFunctions()
0 commit comments