Skip to content

Commit 120f423

Browse files
authored
Merge pull request #7605 from dotty-staging/various-tests
Add more tests and update existing ones
2 parents d6b1762 + 430049b commit 120f423

File tree

8 files changed

+264
-40
lines changed

8 files changed

+264
-40
lines changed

tests/neg/variances1.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import scala.reflect.ClassTag
2+
class Foo[+A: ClassTag](x: A) {
3+
4+
private[this] val elems: Array[A] = Array(x)
5+
6+
def f[B](x: Array[B] = elems): Array[B] = x // error (1) should give a variance error here or ...
7+
8+
}

tests/run/Pouring2.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Vector(Empty(0), Empty(1), Fill(0), Fill(1), Pour(0,1), Pour(1,0))
2+
Fill(1) Pour(1,0) Empty(0) Pour(1,0) Fill(1) Pour(1,0) --> Vector(4, 6)

tests/run/Pouring2.scala

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
class Pouring(capacity: Vector[Int]) {
2+
type Glass = Int
3+
type Content = Vector[Int]
4+
5+
sealed trait Move {
6+
import Move._
7+
def apply(content: Content): Content = this match {
8+
case Empty(g) => content.updated(g, 0)
9+
case Fill(g) => content.updated(g, capacity(g))
10+
case Pour(from, to) =>
11+
val amount = content(from) min (capacity(to) - content(to))
12+
def adjust(s: Content, g: Glass, delta: Int) = s.updated(g, s(g) + delta)
13+
adjust(adjust(content, from, -amount), to, amount)
14+
}
15+
}
16+
object Move {
17+
case class Empty(glass: Glass) extends Move
18+
case class Fill(glass: Glass) extends Move
19+
case class Pour(from: Glass, to: Glass) extends Move
20+
}
21+
22+
val moves: Seq[Move] = {
23+
val glasses = 0 until capacity.length
24+
(for (g <- glasses) yield Move.Empty(g)) ++
25+
(for (g <- glasses) yield Move.Fill(g)) ++
26+
(for (g1 <- glasses; g2 <- glasses if g1 != g2) yield Move.Pour(g1, g2))
27+
}
28+
29+
class Path(history: List[Move], val endContent: Content) {
30+
def extend(move: Move) = new Path(move :: history, move(endContent))
31+
override def toString = s"${history.reverse.mkString(" ")} --> $endContent"
32+
}
33+
34+
val initialContent: Content = capacity.map(x => 0)
35+
val initialPath = new Path(Nil, initialContent)
36+
37+
def from(paths: Set[Path], explored: Set[Content]): LazyList[Set[Path]] =
38+
if (paths.isEmpty) LazyList.empty
39+
else {
40+
val extensions =
41+
for {
42+
path <- paths
43+
move <- moves
44+
next = path.extend(move)
45+
if !explored.contains(next.endContent)
46+
} yield next
47+
paths #:: from(extensions, explored ++ extensions.map(_.endContent))
48+
}
49+
50+
def solutions(target: Int): LazyList[Path] =
51+
for {
52+
paths <- from(Set(initialPath), Set(initialContent))
53+
path <- paths
54+
if path.endContent.contains(target)
55+
} yield path
56+
}
57+
58+
object Test extends App {
59+
val problem = new Pouring(Vector(4, 7))
60+
println(problem.moves)
61+
println(problem.solutions(6).head)
62+
}

tests/run/Signals.check

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
0
2+
10
3+
30

tests/run/Signals.scala

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
2+
import annotation.unchecked._
3+
package frp with
4+
5+
sealed class Signal[+T](expr: (given Signal.Caller) => T) with
6+
private var myExpr: Signal.Caller => T = _
7+
private var myValue: T = _
8+
private var observers: Set[Signal.Caller] = Set()
9+
changeTo(expr)
10+
11+
protected def changeTo(expr: (given Signal.Caller) => T @uncheckedVariance): Unit =
12+
myExpr = (caller => expr(given caller))
13+
computeValue()
14+
15+
def apply()(given caller: Signal.Caller) =
16+
observers += caller
17+
assert(!caller.observers.contains(this), "cyclic signal definition")
18+
myValue
19+
20+
protected def computeValue(): Unit =
21+
val newValue = myExpr(this)
22+
val observeChange = observers.nonEmpty && newValue != myValue
23+
myValue = newValue
24+
if observeChange then
25+
val obs = observers
26+
observers = Set()
27+
obs.foreach(_.computeValue())
28+
29+
object Signal with
30+
type Caller = Signal[?]
31+
given noCaller: Caller(???) with
32+
override def computeValue() = ()
33+
end Signal
34+
35+
class Var[T](expr: (given Signal.Caller) => T) extends Signal[T](expr) with
36+
def update(expr: (given Signal.Caller) => T): Unit = changeTo(expr)
37+
end Var
38+
end frp
39+
40+
import frp._
41+
class BankAccount with
42+
def balance: Signal[Int] = myBalance
43+
44+
private var myBalance: Var[Int] = Var(0)
45+
46+
def deposit(amount: Int): Unit =
47+
if amount > 0 then
48+
val b = myBalance()
49+
myBalance() = b + amount
50+
51+
def withdraw(amount: Int): Int =
52+
if 0 < amount && amount <= balance() then
53+
val b = myBalance()
54+
myBalance() = b - amount
55+
myBalance()
56+
else assertFail("insufficient funds")
57+
end BankAccount
58+
59+
@main def Test() =
60+
def consolidated(accts: List[BankAccount]): Signal[Int] =
61+
Signal(accts.map(_.balance()).sum)
62+
63+
val a = BankAccount()
64+
val b = BankAccount()
65+
val c = consolidated(List(a, b))
66+
println(c())
67+
a.deposit(10)
68+
println(c())
69+
b.deposit(20)
70+
println(c())
71+
end Test

tests/run/Signals1.check

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
0
2+
10
3+
30

tests/run/Signals1.scala

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
2+
import annotation.unchecked._
3+
package frp with
4+
5+
trait Signal[+T] with
6+
def apply()(given caller: Signal.Caller): T
7+
8+
object Signal with
9+
10+
abstract class AbstractSignal[+T] extends Signal[T] with
11+
private var currentValue: T = _
12+
private var observers: Set[Caller] = Set()
13+
14+
protected def eval: Caller => T
15+
16+
protected def computeValue(): Unit =
17+
val newValue = eval(this)
18+
val observeChange = observers.nonEmpty && newValue != currentValue
19+
currentValue = newValue
20+
if observeChange then
21+
val obs = observers
22+
observers = Set()
23+
obs.foreach(_.computeValue())
24+
25+
def apply()(given caller: Caller): T =
26+
observers += caller
27+
assert(!caller.observers.contains(this), "cyclic signal definition")
28+
currentValue
29+
end AbstractSignal
30+
31+
def apply[T](expr: (given Caller) => T): Signal[T] =
32+
new AbstractSignal[T] with
33+
protected val eval = expr(given _)
34+
computeValue()
35+
36+
class Var[T](expr: (given Caller) => T) extends AbstractSignal[T] with
37+
protected var eval: Caller => T = expr(given _)
38+
computeValue()
39+
40+
def update(expr: (given Caller) => T): Unit =
41+
eval = expr(given _)
42+
computeValue()
43+
end Var
44+
45+
opaque type Caller = AbstractSignal[?]
46+
given noCaller: Caller = new AbstractSignal[Nothing] with
47+
override def eval = ???
48+
override def computeValue() = ()
49+
50+
end Signal
51+
end frp
52+
53+
import frp._
54+
class BankAccount with
55+
def balance: Signal[Int] = myBalance
56+
57+
private val myBalance: Signal.Var[Int] = Signal.Var(0)
58+
59+
def deposit(amount: Int): Unit =
60+
if amount > 0 then
61+
val b = myBalance()
62+
myBalance() = b + amount
63+
64+
def withdraw(amount: Int): Int =
65+
if 0 < amount && amount <= balance() then
66+
val b = myBalance()
67+
myBalance() = b - amount
68+
myBalance()
69+
else assertFail("insufficient funds")
70+
end BankAccount
71+
72+
@main def Test() =
73+
def consolidated(accts: List[BankAccount]): Signal[Int] =
74+
Signal(accts.map(_.balance()).sum)
75+
76+
val a = BankAccount()
77+
val b = BankAccount()
78+
val c = consolidated(List(a, b))
79+
println(c())
80+
a.deposit(10)
81+
println(c())
82+
b.deposit(20)
83+
println(c())
84+
end Test

tests/run/instances.scala

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,86 +8,79 @@ object Test extends App {
88

99
case class Circle(x: Double, y: Double, radius: Double)
1010

11-
given circleOps: {
12-
def (c: Circle) circumference: Double = c.radius * math.Pi * 2
13-
}
11+
given circleOps: extension (c: Circle) with
12+
def circumference: Double = c.radius * math.Pi * 2
1413

1514
val circle = new Circle(1, 1, 2.0)
1615

1716
assert(circle.circumference == circleOps.circumference(circle))
1817

19-
given stringOps: {
20-
def (xs: Seq[String]) longestStrings: Seq[String] = {
18+
given stringOps: extension (xs: Seq[String]) with
19+
def longestStrings: Seq[String] =
2120
val maxLength = xs.map(_.length).max
2221
xs.filter(_.length == maxLength)
23-
}
24-
}
22+
2523
val names = List("hi", "hello", "world")
2624
assert(names.longestStrings == List("hello", "world"))
2725

28-
given seqOps: {
29-
def [T](xs: Seq[T]) second = xs.tail.head
30-
}
26+
given extension [T](xs: Seq[T]) with
27+
def second = xs.tail.head
3128

3229
assert(names.longestStrings.second == "world")
3330

34-
given listListOps: {
35-
def [T](xs: List[List[T]]) flattened = xs.foldLeft[List[T]](Nil)(_ ++ _)
36-
}
31+
given listListOps: extension [T](xs: List[List[T]]) with
32+
def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _)
3733

38-
// A right associative op
34+
// A right associative op. Note: can't use given extension for this!
3935
given prepend: {
4036
def [T](x: T) :: (xs: Seq[T]) = x +: xs
4137
}
38+
4239
val ss: Seq[Int] = List(1, 2, 3)
4340
val ss1 = 0 :: ss
4441
assert(ss1 == List(0, 1, 2, 3))
4542

4643
assert(List(names, List("!")).flattened == names :+ "!")
4744
assert(Nil.flattened == Nil)
4845

49-
trait SemiGroup[T] {
46+
trait SemiGroup[T] with
5047
def (x: T) combine (y: T): T
51-
}
52-
trait Monoid[T] extends SemiGroup[T] {
48+
49+
trait Monoid[T] extends SemiGroup[T] with
5350
def unit: T
54-
}
5551

56-
given StringMonoid : Monoid[String] {
52+
given StringMonoid : Monoid[String] with
5753
def (x: String) combine (y: String): String = x.concat(y)
5854
def unit: String = ""
59-
}
6055

6156
// Abstracting over a typeclass with a context bound:
6257
def sum[T: Monoid](xs: List[T]): T =
6358
xs.foldLeft(implicitly[Monoid[T]].unit)(_.combine(_))
6459

6560
println(sum(names))
6661

67-
trait Ord[T] {
62+
trait Ord[T] with
6863
def (x: T) compareTo (y: T): Int
6964
def (x: T) < (y: T) = x.compareTo(y) < 0
7065
def (x: T) > (y: T) = x.compareTo(y) > 0
7166
val minimum: T
72-
}
67+
end Ord
7368

74-
given Ord[Int] {
69+
given Ord[Int] with
7570
def (x: Int) compareTo (y: Int) =
7671
if (x < y) -1 else if (x > y) +1 else 0
7772
val minimum = Int.MinValue
78-
}
7973

80-
given ListOrd[T: Ord] : Ord[List[T]] {
81-
def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match {
74+
given listOrd[T: Ord]: Ord[List[T]] with
75+
def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match
8276
case (Nil, Nil) => 0
8377
case (Nil, _) => -1
8478
case (_, Nil) => +1
8579
case (x :: xs1, y :: ys1) =>
8680
val fst = x.compareTo(y)
8781
if (fst != 0) fst else xs1.compareTo(ys1)
88-
}
8982
val minimum: List[T] = Nil
90-
}
83+
end listOrd
9184

9285
def max[T: Ord](x: T, y: T): T = if (x < y) y else x
9386

@@ -98,35 +91,33 @@ object Test extends App {
9891

9992
println(max(List(1, 2, 3), List(2)))
10093

101-
trait Functor[F[_]] {
94+
trait Functor[F[_]] with
10295
def [A, B](x: F[A]) map (f: A => B): F[B]
103-
}
96+
end Functor
10497

105-
trait Monad[F[_]] extends Functor[F] {
98+
trait Monad[F[_]] extends Functor[F] with
10699
def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B]
107100
def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure)
108101

109102
def pure[A](x: A): F[A]
110-
}
103+
end Monad
111104

112-
given ListMonad : Monad[List] {
105+
given listMonad: Monad[List] with
113106
def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] =
114107
xs.flatMap(f)
115108
def pure[A](x: A): List[A] =
116109
List(x)
117-
}
118110

119-
given ReaderMonad[Ctx] : Monad[[X] =>> Ctx => X] {
111+
given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] with
120112
def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B =
121113
ctx => f(r(ctx))(ctx)
122114
def pure[A](x: A): Ctx => A =
123115
ctx => x
124-
}
125116

126-
def mappAll[F[_]: Monad, T](x: T, fs: List[T => T]): F[T] =
127-
fs.foldLeft(implicitly[Monad[F]].pure(x))((x: F[T], f: T => T) =>
128-
if (true) implicitly[Monad[F]].map(x)(f)
129-
else if (true) x.map(f)
117+
def mapAll[F[_]: Monad, T](x: T, fs: List[T => T]): F[T] =
118+
fs.foldLeft(summon[Monad[F]].pure(x))((x: F[T], f: T => T) =>
119+
if true then summon[Monad[F]].map(x)(f)
120+
else if true then x.map(f)
130121
else x.map[T, T](f)
131122
)
132123
}

0 commit comments

Comments
 (0)