Skip to content

Commit a5556b6

Browse files
authored
Merge pull request #5385 from dotty-staging/port-gestalt-optional
Port of gestalt Optional as a test
2 parents c04eb15 + 7c99a5c commit a5556b6

File tree

4 files changed

+210
-0
lines changed

4 files changed

+210
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/main/scala/gestalt/macros/Optional.scala
2+
// using only `inline`
3+
4+
final class Optional[+A >: Null](val value: A) extends AnyVal {
5+
def get: A = value
6+
def isEmpty = value == null
7+
8+
inline def getOrElse[B >: A](alt: => B): B =
9+
if (isEmpty) alt else value
10+
11+
// f is by name to beta-reduce it
12+
inline def map[B >: Null](f: => A => B): Optional[B] = {
13+
if (isEmpty) new Optional(null)
14+
else new Optional(f(value))
15+
}
16+
17+
override def toString = if (isEmpty) "<empty>" else s"$value"
18+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/test/scala/gestalt/macros/OptionalTest.scala
2+
3+
object Test {
4+
5+
class C
6+
7+
def main(args: Array[String]): Unit = {
8+
getOrElseTest()
9+
mapTest()
10+
simpleGetOrElseTest()
11+
`don't duplicate side-effects of the prefix test`()
12+
hygieneTest()
13+
`owner chain corruptionTest`()
14+
`typed/untyped mixup test`()
15+
`the final thingTest`()
16+
}
17+
18+
def getOrElseTest(): Unit = {
19+
val opt = new Optional[String]("hello")
20+
assert(opt.getOrElse("world") == "hello")
21+
22+
val opt2 = new Optional[String](null)
23+
assert(opt2.getOrElse("hello") == "hello")
24+
}
25+
26+
def mapTest(): Unit = {
27+
val opt = new Optional[String]("hello")
28+
assert(opt.map(_ + " world") == new Optional("hello world"))
29+
30+
val opt2 = new Optional[String](null)
31+
assert(opt2.map(_ + " world") == new Optional(null))
32+
}
33+
34+
def simpleGetOrElseTest(): Unit = {
35+
val c1 = new C
36+
val c2 = new C
37+
val c3 = new C
38+
var sideEffect = 0
39+
40+
val x = new Optional(c1)
41+
val x1 = x.getOrElse(c2)
42+
assert(x1 == c1)
43+
assert(sideEffect == 0)
44+
45+
val y = new Optional(null)
46+
val y1 = y.getOrElse({ sideEffect += 1; c3 })
47+
assert(y1 == c3)
48+
assert(sideEffect == 1)
49+
}
50+
51+
def `don't duplicate side-effects of the prefix test`(): Unit = {
52+
val c1 = new C
53+
val c2 = new C
54+
var sideEffect = 0
55+
56+
def x = { sideEffect += 1; new Optional(c1) }
57+
val x1 = x.getOrElse(c2)
58+
assert(sideEffect == 1)
59+
}
60+
61+
def hygieneTest(): Unit = {
62+
val temp = 100
63+
new Optional(if (temp < 100) new C else null).getOrElse(new C)
64+
}
65+
66+
def `owner chain corruptionTest`(): Unit = {
67+
def foo(x: => Optional[C]) = x
68+
foo({ val y = new Optional(null); y }).getOrElse(new C)
69+
}
70+
71+
def `typed/untyped mixup test`(): Unit = {
72+
val x1 = new Optional(new C)
73+
val x2 = x1.map(_.toString)
74+
}
75+
76+
def `the final thingTest`(): Unit = {
77+
def foo(f: => C): C = f
78+
val x1 = new Optional(new C)
79+
val x2 = x1.map(x => foo({ val y = x; y }))
80+
}
81+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/main/scala/gestalt/macros/Optional.scala
2+
// using staging macros (only quotes and splices)
3+
4+
import scala.quoted._
5+
6+
final class Optional[+A >: Null](val value: A) extends AnyVal {
7+
def get: A = value
8+
def isEmpty = value == null
9+
10+
inline def getOrElse[B >: A](alt: => B): B = ~Optional.getOrElseImpl('(this), '(alt))
11+
12+
inline def map[B >: Null](f: A => B): Optional[B] = ~Optional.mapImpl('(this), '(f))
13+
14+
override def toString = if (isEmpty) "<empty>" else s"$value"
15+
}
16+
17+
object Optional {
18+
19+
// FIXME fix issue #5097 and enable private
20+
/*private*/ def getOrElseImpl[T >: Null](opt: Expr[Optional[T]], alt: Expr[T]): Expr[T] = '{
21+
if ((~opt).isEmpty) ~alt else (~opt).value
22+
}
23+
24+
// FIXME fix issue #5097 and enable private
25+
/*private*/ def mapImpl[A >: Null, B >: Null : Type](opt: Expr[Optional[A]], f: Expr[A => B]): Expr[Optional[B]] = '{
26+
if ((~opt).isEmpty) new Optional(null)
27+
else new Optional(~f('((~opt).value)))
28+
}
29+
30+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/test/scala/gestalt/macros/OptionalTest.scala
2+
3+
object Test {
4+
5+
class C
6+
7+
def main(args: Array[String]): Unit = {
8+
getOrElseTest()
9+
mapTest()
10+
simpleGetOrElseTest()
11+
`don't duplicate side-effects of the prefix test`()
12+
hygieneTest()
13+
`owner chain corruptionTest`()
14+
`typed/untyped mixup test`()
15+
`the final thingTest`()
16+
}
17+
18+
def getOrElseTest(): Unit = {
19+
val opt = new Optional[String]("hello")
20+
assert(opt.getOrElse("world") == "hello")
21+
22+
val opt2 = new Optional[String](null)
23+
assert(opt2.getOrElse("hello") == "hello")
24+
}
25+
26+
def mapTest(): Unit = {
27+
val opt = new Optional[String]("hello")
28+
assert(opt.map(_ + " world") == new Optional("hello world"))
29+
30+
val opt2 = new Optional[String](null)
31+
assert(opt2.map(_ + " world") == new Optional(null))
32+
}
33+
34+
def simpleGetOrElseTest(): Unit = {
35+
val c1 = new C
36+
val c2 = new C
37+
val c3 = new C
38+
var sideEffect = 0
39+
40+
val x = new Optional(c1)
41+
val x1 = x.getOrElse(c2)
42+
assert(x1 == c1)
43+
assert(sideEffect == 0)
44+
45+
val y = new Optional(null)
46+
val y1 = y.getOrElse({ sideEffect += 1; c3 })
47+
assert(y1 == c3)
48+
assert(sideEffect == 1)
49+
}
50+
51+
def `don't duplicate side-effects of the prefix test`(): Unit = {
52+
val c1 = new C
53+
val c2 = new C
54+
var sideEffect = 0
55+
56+
def x = { sideEffect += 1; new Optional(c1) }
57+
val x1 = x.getOrElse(c2)
58+
assert(sideEffect == 1)
59+
}
60+
61+
def hygieneTest(): Unit = {
62+
val temp = 100
63+
new Optional(if (temp < 100) new C else null).getOrElse(new C)
64+
}
65+
66+
def `owner chain corruptionTest`(): Unit = {
67+
def foo(x: => Optional[C]) = x
68+
foo({ val y = new Optional(null); y }).getOrElse(new C)
69+
}
70+
71+
def `typed/untyped mixup test`(): Unit = {
72+
val x1 = new Optional(new C)
73+
val x2 = x1.map(_.toString)
74+
}
75+
76+
def `the final thingTest`(): Unit = {
77+
def foo(f: => C): C = f
78+
val x1 = new Optional(new C)
79+
val x2 = x1.map(x => foo({ val y = x; y }))
80+
}
81+
}

0 commit comments

Comments
 (0)