diff --git a/CHANGELOG.md b/CHANGELOG.md index d8e3f9d..6aeb507 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,3 +84,9 @@ change. * Update Shapeless to 2.3.3 * Update Scalaz to 7.2.18 * Update Gerweck Utils to 2.7.2 + +### 0.14 + +* Migration from Scalaz to Cats + * This is the library we use to provide `Monad` and `Applicative` instances + for observable values. diff --git a/build.sbt b/build.sbt index c8974e1..47e8407 100644 --- a/build.sbt +++ b/build.sbt @@ -19,7 +19,7 @@ lazy val root = (project in file (".")) gerweckUtil, scalaJava8, scalaFx, - scalaz, + cats, shapeless ), diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 48a7241..86e2afd 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -24,7 +24,7 @@ object Dependencies { final val scalaParserVersion = "1.0.4" final val scalaXmlVersion = "1.0.5" final val gerweckUtilVersion = "2.7.2" - final val scalazVersion = "7.2.18" + final val catsVersion = "1.0.1" final val shapelessVersion = "2.3.3" final val scallopVersion = "1.0.1" @@ -39,7 +39,7 @@ object Dependencies { val gerweckUtil = "org.gerweck.scala" %% "gerweck-utils" % gerweckUtilVersion val gerweckUtilAkka = "org.gerweck.scala" %% "gerweck-utils-akka" % gerweckUtilVersion val scalaJava8 = "org.scala-lang.modules" %% "scala-java8-compat" % scalaJava8Version - val scalaz = "org.scalaz" %% "scalaz-core" % scalazVersion + val cats = "org.typelevel" %% "cats-core" % catsVersion val shapeless = "com.chuusai" %% "shapeless" % shapelessVersion val scallop = "org.rogach" %% "scallop" % scallopVersion diff --git a/project/ProjectSettings.scala b/project/ProjectSettings.scala index ad456d0..841ec3f 100644 --- a/project/ProjectSettings.scala +++ b/project/ProjectSettings.scala @@ -15,6 +15,7 @@ trait ProjectSettings override final val extraScalaVersions = Seq("2.11.11") override final val defaultOptimize = true override final val defaultOptimizeGlobal = false + override final val extraScalacOptions = Seq("-Ypartial-unification") override final val sonatypeResolver = true diff --git a/src/main/scala/org/gerweck/scalafx/util/observable.scala b/src/main/scala/org/gerweck/scalafx/util/observable.scala index 663231b..d145c81 100644 --- a/src/main/scala/org/gerweck/scalafx/util/observable.scala +++ b/src/main/scala/org/gerweck/scalafx/util/observable.scala @@ -2,7 +2,7 @@ package org.gerweck.scalafx.util import language.implicitConversions -import scalaz._ +import cats._ import scalafx.beans.property._ import scalafx.beans.value._ @@ -16,7 +16,7 @@ trait ObservableImplicits { * a property in a tight loop, I expect you'll have bigger performance * issues.) */ - implicit val observableInstances = new Applicative[Observable] with Functor[Observable] with Monad[Observable] { + implicit object observableInstances extends Applicative[Observable] with Functor[Observable] with Monad[Observable] { /* Map can be derived from `ap`, but this adds less overhead. */ override def map[A, B](a: Observable[A])(f: A => B): ReadOnlyObjectProperty[B] = { @inline def recalculate(): B = f(a.value) @@ -38,12 +38,12 @@ trait ObservableImplicits { prop } - def point[A](a: => A): ReadOnlyObjectProperty[A] = { + override def pure[A](a: A): ReadOnlyObjectProperty[A] = { ObjectProperty[A](a) } /* Ap can be derived from `point` and `bind`, but this has less overhead. */ - override def ap[A, B](fa: => Observable[A])(f: => Observable[A => B]): ReadOnlyObjectProperty[B] = { + override def ap[A, B](f: Observable[A => B])(fa: Observable[A]): ReadOnlyObjectProperty[B] = { @inline def recalculate(): B = (f.value)(fa.value) val originalValue = recalculate() @@ -66,13 +66,18 @@ trait ObservableImplicits { prop } - /* Aka `flatMap` */ - override def bind[A, B](fa: Observable[A])(f: A => Observable[B]): ReadOnlyObjectProperty[B] = { - join(map(fa)(f)) + override def flatMap[A, B](fa: Observable[A])(f: A => Observable[B]): ReadOnlyObjectProperty[B] = { + flatten(map(fa)(f)) } - /* Aka `flatten` */ - override def join[A](ooa: Observable[Observable[A]]): ReadOnlyObjectProperty[A] = { + override def tailRecM[A, B](a: A)(f: A => Observable[Either[A, B]]): Observable[B] = { + this.flatMap(f(a)) { + case Right(b) => pure(b) + case Left(nextA) => tailRecM(nextA)(f) + } + } + + override def flatten[A](ooa: Observable[Observable[A]]): ReadOnlyObjectProperty[A] = { @inline def oa() = ooa.value @inline def calc(): A = oa().value @@ -109,12 +114,18 @@ trait ObservableImplicits { } } - implicit val readOnlyObjectPropertyInstances = new Applicative[ReadOnlyObjectProperty] with Functor[ReadOnlyObjectProperty] with Monad[ReadOnlyObjectProperty] { + implicit object readOnlyObjectPropertyInstances extends Applicative[ReadOnlyObjectProperty] with Functor[ReadOnlyObjectProperty] with Monad[ReadOnlyObjectProperty] { override def map[A, B](a: ReadOnlyObjectProperty[A])(f: A => B): ReadOnlyObjectProperty[B] = observableInstances.map(a)(f) - override def point[A](a: => A): ReadOnlyObjectProperty[A] = observableInstances.point(a) - override def ap[A, B](fa: => ReadOnlyObjectProperty[A])(f: => ReadOnlyObjectProperty[A => B]): ReadOnlyObjectProperty[B] = observableInstances.ap(fa)(f) - override def bind[A, B](fa: ReadOnlyObjectProperty[A])(f: A => ReadOnlyObjectProperty[B]): ReadOnlyObjectProperty[B] = observableInstances.bind(fa)(f) - override def join[A](ooa: ReadOnlyObjectProperty[ReadOnlyObjectProperty[A]]): ReadOnlyObjectProperty[A] = { + override def pure[A](a: A): ReadOnlyObjectProperty[A] = observableInstances.pure(a) + override def ap[A, B](f: ReadOnlyObjectProperty[A => B])(fa: ReadOnlyObjectProperty[A]): ReadOnlyObjectProperty[B] = observableInstances.ap(f)(fa) + override def flatMap[A, B](fa: ReadOnlyObjectProperty[A])(f: A => ReadOnlyObjectProperty[B]): ReadOnlyObjectProperty[B] = observableInstances.flatMap(fa)(f) + override def tailRecM[A, B](a: A)(f: A => ReadOnlyObjectProperty[Either[A, B]]): ReadOnlyObjectProperty[B] = { + flatMap(f(a)) { + case Right(b) => pure(b) + case Left(nextA) => tailRecM(nextA)(f) + } + } + override def flatten[A](ooa: ReadOnlyObjectProperty[ReadOnlyObjectProperty[A]]): ReadOnlyObjectProperty[A] = { /* NOTE: this is copy-pasted from `observableInstances`. TBD: Find a way to share this. */ @inline def oa() = ooa.value @inline def calc(): A = oa().value @@ -197,11 +208,11 @@ final class RichObservable[A, C](val self: ObservableValue[A, C]) extends AnyVal @inline private def oapp = observableInstances def map[B](f: A => B) = oapp.map(self)(f) - def flatMap[B](f: A => Observable[B]) = oapp.bind(self)(f) - def <*>[B](f: Observable[A => B]): ObservableValue[B, B] = oapp.ap(self)(f) + def flatMap[B](f: A => Observable[B]) = oapp.flatMap(self)(f) + def <*>[B](f: Observable[A => B]): ObservableValue[B, B] = oapp.ap(f)(self) def tuple[B](f: Observable[B]): Observable[(A,B)] = oapp.tuple2(self, f) - final def *>[B](fb: ObjObs[B]): Observable[B] = oapp.apply2(self,fb)((_,b) => b) - final def <*[B](fb: ObjObs[B]): Observable[A] = oapp.apply2(self,fb)((a,_) => a) + final def *>[B](fb: ObjObs[B]): Observable[B] = oapp.map2(self,fb)((_,b) => b) + final def <*[B](fb: ObjObs[B]): Observable[A] = oapp.map2(self,fb)((a,_) => a) final def |@|[B, B1](fb: ObservableValue[B, B1]) = ObservableTupler(self, fb) diff --git a/src/main/scala/org/gerweck/scalafx/util/package.scala b/src/main/scala/org/gerweck/scalafx/util/package.scala index 3b4dc8d..4729a5b 100644 --- a/src/main/scala/org/gerweck/scalafx/util/package.scala +++ b/src/main/scala/org/gerweck/scalafx/util/package.scala @@ -8,8 +8,6 @@ import scalafx.scene.input._ import scalafx.scene.layout.GridPane import scalafx.scene.text.Text -import scalaz._ - /** Various implicits and global utilities for ScalaFX work. * * @author Sarah Gerweck