Skip to content

Add more tests and update existing ones #7605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions tests/neg/variances1.scala
Original file line number Diff line number Diff line change
@@ -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 ...

}
2 changes: 2 additions & 0 deletions tests/run/Pouring2.check
Original file line number Diff line number Diff line change
@@ -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)
62 changes: 62 additions & 0 deletions tests/run/Pouring2.scala
Original file line number Diff line number Diff line change
@@ -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)
}
3 changes: 3 additions & 0 deletions tests/run/Signals.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0
10
30
71 changes: 71 additions & 0 deletions tests/run/Signals.scala
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions tests/run/Signals1.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0
10
30
84 changes: 84 additions & 0 deletions tests/run/Signals1.scala
Original file line number Diff line number Diff line change
@@ -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
71 changes: 31 additions & 40 deletions tests/run/instances.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,86 +8,79 @@ 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))

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 =
xs.foldLeft(implicitly[Monoid[T]].unit)(_.combine(_))

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

Expand All @@ -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)
)
}