From 25afed89d825b9f7168f76d6b7faca0a72639f98 Mon Sep 17 00:00:00 2001 From: Shadaj Laddad Date: Fri, 10 Nov 2017 19:46:36 -0800 Subject: [PATCH] Make Stream covariant to match regular collections (#126) * Make Stream covariant to match regular collections This should fix the weird typing issues we encountered earlier, such as not being able to view a stream of `None`s as a stream of `Option`s. * Remove unused `Value` type member * Add unit test for covariant behavior --- .../commons/drivetrain/PurePursuitControllerTests.scala | 2 +- .../com/lynbrookrobotics/potassium/streams/Stream.scala | 7 +++---- .../lynbrookrobotics/potassium/streams/StreamTest.scala | 6 ++++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/commons/src/test/scala/com/lynbrookrobotics/potassium/commons/drivetrain/PurePursuitControllerTests.scala b/commons/src/test/scala/com/lynbrookrobotics/potassium/commons/drivetrain/PurePursuitControllerTests.scala index 66647d07..b8a8e3aa 100644 --- a/commons/src/test/scala/com/lynbrookrobotics/potassium/commons/drivetrain/PurePursuitControllerTests.scala +++ b/commons/src/test/scala/com/lynbrookrobotics/potassium/commons/drivetrain/PurePursuitControllerTests.scala @@ -212,7 +212,7 @@ class PurePursuitControllerTests extends FunSuite { val target = Point(Feet(1), Feet(1)) val position = hardware.turnPosition.mapToConstant(Point(Feet(2), Feet(2))) - val path: Stream[(Segment, Option[Segment])] = hardware.turnPosition.mapToConstant((Segment(origin, target), None)) + val path = hardware.turnPosition.mapToConstant((Segment(origin, target), None)) val output = controllers.purePursuitControllerTurn( hardware.turnPosition.mapToConstant(Degrees(45)), diff --git a/core/shared/src/main/scala/com/lynbrookrobotics/potassium/streams/Stream.scala b/core/shared/src/main/scala/com/lynbrookrobotics/potassium/streams/Stream.scala index d8294a7c..b1f57784 100644 --- a/core/shared/src/main/scala/com/lynbrookrobotics/potassium/streams/Stream.scala +++ b/core/shared/src/main/scala/com/lynbrookrobotics/potassium/streams/Stream.scala @@ -9,18 +9,17 @@ import squants.time.{Time, TimeDerivative, TimeIntegral} import scala.collection.immutable.Queue import scala.ref.WeakReference import com.lynbrookrobotics.potassium.Platform -import com.lynbrookrobotics.potassium.events.{ContinuousEvent, ImpulseEvent} -abstract class Stream[T] { self => - final type Value = T +import scala.annotation.unchecked.uncheckedVariance +abstract class Stream[+T] { self => val expectedPeriodicity: ExpectedPeriodicity val originTimeStream: Option[Stream[Time]] private[this] var listeners = Vector.empty[T => Unit] - protected def publishValue(value: T): Unit = { + protected def publishValue(value: T @uncheckedVariance): Unit = { listeners.foreach(_.apply(value)) // TODO: more stuff maybe } diff --git a/core/shared/src/test/scala/com/lynbrookrobotics/potassium/streams/StreamTest.scala b/core/shared/src/test/scala/com/lynbrookrobotics/potassium/streams/StreamTest.scala index c15b35fb..1f4eda12 100644 --- a/core/shared/src/test/scala/com/lynbrookrobotics/potassium/streams/StreamTest.scala +++ b/core/shared/src/test/scala/com/lynbrookrobotics/potassium/streams/StreamTest.scala @@ -12,6 +12,12 @@ import scala.concurrent.duration.Duration import scala.concurrent.{Await, Promise} class StreamTest extends FunSuite { + test("Streams can be used as a stream of a parent type of the original element type (covariance)") { + val origStream: Stream[None.type] = Stream.manual[None.type]._1 + + assertCompiles("origStream: Stream[Option[Int]]") + } + test("Manually created stream runs callbacks appropriately") { val (str, pub) = Stream.manual[Int]