Skip to content

Commit c454d25

Browse files
committed
New example of intersection and union types.
Signed-off-by: Dean Wampler <dean.wampler@ibm.com>
1 parent 96b0971 commit c454d25

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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

Comments
 (0)