diff --git a/tests/neg/variances1.scala b/tests/neg/variances1.scala new file mode 100644 index 000000000000..aabfd32565e9 --- /dev/null +++ b/tests/neg/variances1.scala @@ -0,0 +1,8 @@ +import scala.reflect.ClassTag +class Foo[+A: ClassTag](x: A) { + + private[this] val elems: Array[A] = Array(x) + + def f[B](x: Array[B] = elems): Array[B] = x // error (1) should give a variance error here or ... + +} \ No newline at end of file diff --git a/tests/run/Pouring2.check b/tests/run/Pouring2.check new file mode 100644 index 000000000000..f07f29105c0b --- /dev/null +++ b/tests/run/Pouring2.check @@ -0,0 +1,2 @@ +Vector(Empty(0), Empty(1), Fill(0), Fill(1), Pour(0,1), Pour(1,0)) +Fill(1) Pour(1,0) Empty(0) Pour(1,0) Fill(1) Pour(1,0) --> Vector(4, 6) diff --git a/tests/run/Pouring2.scala b/tests/run/Pouring2.scala new file mode 100644 index 000000000000..d0d5241431da --- /dev/null +++ b/tests/run/Pouring2.scala @@ -0,0 +1,62 @@ +class Pouring(capacity: Vector[Int]) { + type Glass = Int + type Content = Vector[Int] + + sealed trait Move { + import Move._ + def apply(content: Content): Content = this match { + case Empty(g) => content.updated(g, 0) + case Fill(g) => content.updated(g, capacity(g)) + case Pour(from, to) => + val amount = content(from) min (capacity(to) - content(to)) + def adjust(s: Content, g: Glass, delta: Int) = s.updated(g, s(g) + delta) + adjust(adjust(content, from, -amount), to, amount) + } + } + object Move { + case class Empty(glass: Glass) extends Move + case class Fill(glass: Glass) extends Move + case class Pour(from: Glass, to: Glass) extends Move + } + + val moves: Seq[Move] = { + val glasses = 0 until capacity.length + (for (g <- glasses) yield Move.Empty(g)) ++ + (for (g <- glasses) yield Move.Fill(g)) ++ + (for (g1 <- glasses; g2 <- glasses if g1 != g2) yield Move.Pour(g1, g2)) + } + + class Path(history: List[Move], val endContent: Content) { + def extend(move: Move) = new Path(move :: history, move(endContent)) + override def toString = s"${history.reverse.mkString(" ")} --> $endContent" + } + + val initialContent: Content = capacity.map(x => 0) + val initialPath = new Path(Nil, initialContent) + + def from(paths: Set[Path], explored: Set[Content]): LazyList[Set[Path]] = + if (paths.isEmpty) LazyList.empty + else { + val extensions = + for { + path <- paths + move <- moves + next = path.extend(move) + if !explored.contains(next.endContent) + } yield next + paths #:: from(extensions, explored ++ extensions.map(_.endContent)) + } + + def solutions(target: Int): LazyList[Path] = + for { + paths <- from(Set(initialPath), Set(initialContent)) + path <- paths + if path.endContent.contains(target) + } yield path +} + +object Test extends App { + val problem = new Pouring(Vector(4, 7)) + println(problem.moves) + println(problem.solutions(6).head) +} \ No newline at end of file diff --git a/tests/run/Signals.check b/tests/run/Signals.check new file mode 100644 index 000000000000..229f6b1ffb6b --- /dev/null +++ b/tests/run/Signals.check @@ -0,0 +1,3 @@ +0 +10 +30 diff --git a/tests/run/Signals.scala b/tests/run/Signals.scala new file mode 100644 index 000000000000..e320b579ad41 --- /dev/null +++ b/tests/run/Signals.scala @@ -0,0 +1,71 @@ + +import annotation.unchecked._ +package frp with + + sealed class Signal[+T](expr: (given Signal.Caller) => T) with + private var myExpr: Signal.Caller => T = _ + private var myValue: T = _ + private var observers: Set[Signal.Caller] = Set() + changeTo(expr) + + protected def changeTo(expr: (given Signal.Caller) => T @uncheckedVariance): Unit = + myExpr = (caller => expr(given caller)) + computeValue() + + def apply()(given caller: Signal.Caller) = + observers += caller + assert(!caller.observers.contains(this), "cyclic signal definition") + myValue + + protected def computeValue(): Unit = + val newValue = myExpr(this) + val observeChange = observers.nonEmpty && newValue != myValue + myValue = newValue + if observeChange then + val obs = observers + observers = Set() + obs.foreach(_.computeValue()) + + object Signal with + type Caller = Signal[?] + given noCaller: Caller(???) with + override def computeValue() = () + end Signal + + class Var[T](expr: (given Signal.Caller) => T) extends Signal[T](expr) with + def update(expr: (given Signal.Caller) => T): Unit = changeTo(expr) + end Var +end frp + +import frp._ +class BankAccount with + def balance: Signal[Int] = myBalance + + private var myBalance: Var[Int] = Var(0) + + def deposit(amount: Int): Unit = + if amount > 0 then + val b = myBalance() + myBalance() = b + amount + + def withdraw(amount: Int): Int = + if 0 < amount && amount <= balance() then + val b = myBalance() + myBalance() = b - amount + myBalance() + else assertFail("insufficient funds") +end BankAccount + +@main def Test() = + def consolidated(accts: List[BankAccount]): Signal[Int] = + Signal(accts.map(_.balance()).sum) + + val a = BankAccount() + val b = BankAccount() + val c = consolidated(List(a, b)) + println(c()) + a.deposit(10) + println(c()) + b.deposit(20) + println(c()) +end Test diff --git a/tests/run/Signals1.check b/tests/run/Signals1.check new file mode 100644 index 000000000000..229f6b1ffb6b --- /dev/null +++ b/tests/run/Signals1.check @@ -0,0 +1,3 @@ +0 +10 +30 diff --git a/tests/run/Signals1.scala b/tests/run/Signals1.scala new file mode 100644 index 000000000000..0c461652af4e --- /dev/null +++ b/tests/run/Signals1.scala @@ -0,0 +1,84 @@ + +import annotation.unchecked._ +package frp with + + trait Signal[+T] with + def apply()(given caller: Signal.Caller): T + + object Signal with + + abstract class AbstractSignal[+T] extends Signal[T] with + private var currentValue: T = _ + private var observers: Set[Caller] = Set() + + protected def eval: Caller => T + + protected def computeValue(): Unit = + val newValue = eval(this) + val observeChange = observers.nonEmpty && newValue != currentValue + currentValue = newValue + if observeChange then + val obs = observers + observers = Set() + obs.foreach(_.computeValue()) + + def apply()(given caller: Caller): T = + observers += caller + assert(!caller.observers.contains(this), "cyclic signal definition") + currentValue + end AbstractSignal + + def apply[T](expr: (given Caller) => T): Signal[T] = + new AbstractSignal[T] with + protected val eval = expr(given _) + computeValue() + + class Var[T](expr: (given Caller) => T) extends AbstractSignal[T] with + protected var eval: Caller => T = expr(given _) + computeValue() + + def update(expr: (given Caller) => T): Unit = + eval = expr(given _) + computeValue() + end Var + + opaque type Caller = AbstractSignal[?] + given noCaller: Caller = new AbstractSignal[Nothing] with + override def eval = ??? + override def computeValue() = () + + end Signal +end frp + +import frp._ +class BankAccount with + def balance: Signal[Int] = myBalance + + private val myBalance: Signal.Var[Int] = Signal.Var(0) + + def deposit(amount: Int): Unit = + if amount > 0 then + val b = myBalance() + myBalance() = b + amount + + def withdraw(amount: Int): Int = + if 0 < amount && amount <= balance() then + val b = myBalance() + myBalance() = b - amount + myBalance() + else assertFail("insufficient funds") +end BankAccount + +@main def Test() = + def consolidated(accts: List[BankAccount]): Signal[Int] = + Signal(accts.map(_.balance()).sum) + + val a = BankAccount() + val b = BankAccount() + val c = consolidated(List(a, b)) + println(c()) + a.deposit(10) + println(c()) + b.deposit(20) + println(c()) +end Test diff --git a/tests/run/instances.scala b/tests/run/instances.scala index 482d7dc3ef24..aeb85d01d624 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -8,37 +8,34 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - given circleOps: { - def (c: Circle) circumference: Double = c.radius * math.Pi * 2 - } + given circleOps: extension (c: Circle) with + def circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) assert(circle.circumference == circleOps.circumference(circle)) - given stringOps: { - def (xs: Seq[String]) longestStrings: Seq[String] = { + given stringOps: extension (xs: Seq[String]) with + def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - } - } + val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - given seqOps: { - def [T](xs: Seq[T]) second = xs.tail.head - } + given extension [T](xs: Seq[T]) with + def second = xs.tail.head assert(names.longestStrings.second == "world") - given listListOps: { - def [T](xs: List[List[T]]) flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) - } + given listListOps: extension [T](xs: List[List[T]]) with + def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) - // A right associative op + // A right associative op. Note: can't use given extension for this! given prepend: { def [T](x: T) :: (xs: Seq[T]) = x +: xs } + val ss: Seq[Int] = List(1, 2, 3) val ss1 = 0 :: ss assert(ss1 == List(0, 1, 2, 3)) @@ -46,17 +43,15 @@ object Test extends App { assert(List(names, List("!")).flattened == names :+ "!") assert(Nil.flattened == Nil) - trait SemiGroup[T] { + trait SemiGroup[T] with def (x: T) combine (y: T): T - } - trait Monoid[T] extends SemiGroup[T] { + + trait Monoid[T] extends SemiGroup[T] with def unit: T - } - given StringMonoid : Monoid[String] { + given StringMonoid : Monoid[String] with def (x: String) combine (y: String): String = x.concat(y) def unit: String = "" - } // Abstracting over a typeclass with a context bound: def sum[T: Monoid](xs: List[T]): T = @@ -64,30 +59,28 @@ object Test extends App { println(sum(names)) - trait Ord[T] { + trait Ord[T] with def (x: T) compareTo (y: T): Int def (x: T) < (y: T) = x.compareTo(y) < 0 def (x: T) > (y: T) = x.compareTo(y) > 0 val minimum: T - } + end Ord - given Ord[Int] { + given Ord[Int] with def (x: Int) compareTo (y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue - } - given ListOrd[T: Ord] : Ord[List[T]] { - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match { + given listOrd[T: Ord]: Ord[List[T]] with + def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 case (x :: xs1, y :: ys1) => val fst = x.compareTo(y) if (fst != 0) fst else xs1.compareTo(ys1) - } val minimum: List[T] = Nil - } + end listOrd def max[T: Ord](x: T, y: T): T = if (x < y) y else x @@ -98,35 +91,33 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) - trait Functor[F[_]] { + trait Functor[F[_]] with def [A, B](x: F[A]) map (f: A => B): F[B] - } + end Functor - trait Monad[F[_]] extends Functor[F] { + trait Monad[F[_]] extends Functor[F] with def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] - } + end Monad - given ListMonad : Monad[List] { + given listMonad: Monad[List] with def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) - } - given ReaderMonad[Ctx] : Monad[[X] =>> Ctx => X] { + given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] with def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x - } - def mappAll[F[_]: Monad, T](x: T, fs: List[T => T]): F[T] = - fs.foldLeft(implicitly[Monad[F]].pure(x))((x: F[T], f: T => T) => - if (true) implicitly[Monad[F]].map(x)(f) - else if (true) x.map(f) + def mapAll[F[_]: Monad, T](x: T, fs: List[T => T]): F[T] = + fs.foldLeft(summon[Monad[F]].pure(x))((x: F[T], f: T => T) => + if true then summon[Monad[F]].map(x)(f) + else if true then x.map(f) else x.map[T, T](f) ) } \ No newline at end of file