From 741a5710930d68f726d3e5266367c355f4fe7b17 Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Wed, 30 Sep 2015 10:49:47 +0100 Subject: [PATCH 1/2] Added support for export-hook; added ConsK. --- base.sbt | 1 - build.sbt | 6 ++++-- core/src/main/scala/alleycats/ConsK.scala | 20 +++++++++++++++++++ core/src/main/scala/alleycats/Empty.scala | 7 ++++++- core/src/main/scala/alleycats/EmptyK.scala | 14 +++++++++++++ core/src/main/scala/alleycats/Extract.scala | 8 +++++++- core/src/main/scala/alleycats/One.scala | 8 +++++++- core/src/main/scala/alleycats/Pure.scala | 8 +++++++- core/src/main/scala/alleycats/Zero.scala | 8 +++++++- core/src/main/scala/alleycats/std/all.scala | 17 ++++++++++------ core/src/main/scala/alleycats/std/list.scala | 17 +++++++++++++--- .../src/main/scala/alleycats/std/option.scala | 11 +++++++--- core/src/main/scala/alleycats/std/set.scala | 19 ++++++++++++++---- core/src/main/scala/alleycats/std/try.scala | 15 +++++++++++--- 14 files changed, 132 insertions(+), 27 deletions(-) create mode 100644 core/src/main/scala/alleycats/ConsK.scala diff --git a/base.sbt b/base.sbt index 723222f..8226c4a 100644 --- a/base.sbt +++ b/base.sbt @@ -1,2 +1 @@ addCommandAlias("gitSnapshots", ";set version in ThisBuild := git.gitDescribedVersion.value.get + \"-SNAPSHOT\"") -enablePlugins(GitBranchPrompt) diff --git a/build.sbt b/build.sbt index 64dedcc..c458839 100644 --- a/build.sbt +++ b/build.sbt @@ -27,6 +27,7 @@ val license = ("MIT", url("http://opensource.org/licenses/MIT")) val catsVersion = "0.2.0" val disciplineVersion = "0.4" +val exportHookVersion = "1.0.3-SNAPSHOT" val kindProjectorVersion = "0.6.3" val machinistVersion = "0.4.1" val macroCompatVersion = "1.0.2" @@ -48,13 +49,14 @@ lazy val buildSettings = Seq( lazy val commonSettings = sharedCommonSettings ++ Seq( scalacOptions ++= commonScalacOptions, parallelExecution in Test := false, - // resolvers += Resolver.sonatypeRepo("snapshots") + resolvers += Resolver.sonatypeRepo("snapshots"), libraryDependencies ++= Seq( "com.github.mpilquist" %%% "simulacrum" % simulacrumVersion, + "org.typelevel" %%% "export-hook" % exportHookVersion, "org.typelevel" %%% "machinist" % machinistVersion, compilerPlugin("org.spire-math" %% "kind-projector" % kindProjectorVersion) ) -) ++ warnUnusedImport ++ unidocCommonSettings +) /* ++ warnUnusedImport */ ++ unidocCommonSettings // -Ywarn-unused-import doesn't like macro annotations lazy val commonJsSettings = Seq( scalaJSStage in Global := FastOptStage diff --git a/core/src/main/scala/alleycats/ConsK.scala b/core/src/main/scala/alleycats/ConsK.scala new file mode 100644 index 0000000..0661532 --- /dev/null +++ b/core/src/main/scala/alleycats/ConsK.scala @@ -0,0 +1,20 @@ +package alleycats + +import cats.SemigroupK +import export.imports +import simulacrum.typeclass + +@typeclass trait ConsK[F[_]] { + def cons[A](hd: A, tl: F[A]): F[A] +} + +object ConsK extends ConsK0 { + implicit def pureSemigroupKIsConsK[F[_]](implicit p: Pure[F], s: SemigroupK[F]): ConsK[F] = + new ConsK[F] { + def cons[A](hd: A, tl: F[A]): F[A] = s.combine(p.pure(hd), tl) + } +} + +@imports[ConsK] +trait ConsK0 + diff --git a/core/src/main/scala/alleycats/Empty.scala b/core/src/main/scala/alleycats/Empty.scala index 5d295c2..b74ea2b 100644 --- a/core/src/main/scala/alleycats/Empty.scala +++ b/core/src/main/scala/alleycats/Empty.scala @@ -2,6 +2,7 @@ package alleycats import cats.{Eq, Monoid} import cats.syntax.eq._ +import export.imports import simulacrum.typeclass import scala.collection.generic.CanBuildFrom @@ -25,7 +26,11 @@ trait EmptyInstances0 extends EmptyInstances1 { Empty(cbf().result) } -trait EmptyInstances1 { +trait EmptyInstances1 extends EmptyInstances2 { + // If Monoid extended Empty then this could be an exported subclass instance provided by Monoid implicit def monoidIsEmpty[A: Monoid]: Empty[A] = Empty(Monoid[A].empty) } + +@imports[Empty] +trait EmptyInstances2 diff --git a/core/src/main/scala/alleycats/EmptyK.scala b/core/src/main/scala/alleycats/EmptyK.scala index 8854e1c..f9ebafd 100644 --- a/core/src/main/scala/alleycats/EmptyK.scala +++ b/core/src/main/scala/alleycats/EmptyK.scala @@ -1,5 +1,6 @@ package alleycats +import export._ import simulacrum.typeclass @typeclass trait EmptyK[F[_]] { self => @@ -10,3 +11,16 @@ import simulacrum.typeclass def empty: F[A] = self.empty[A] } } + +object EmptyK extends EmptyK0 + +// Currently a simulacrum bug prevents this trait from being folded into +// the object +@imports[EmptyK] +trait EmptyK0 + +@exports +object EmptyKInstances { + @export(Instantiated) + implicit def instantiate[F[_], T](implicit ekf: EmptyK[F]): Empty[F[T]] = ekf.synthesize[T] +} diff --git a/core/src/main/scala/alleycats/Extract.scala b/core/src/main/scala/alleycats/Extract.scala index 7f5bfce..04482f1 100644 --- a/core/src/main/scala/alleycats/Extract.scala +++ b/core/src/main/scala/alleycats/Extract.scala @@ -1,18 +1,21 @@ package alleycats import cats.{Applicative, CoflatMap, Comonad} +import export.imports import simulacrum.typeclass @typeclass trait Extract[F[_]] { def extract[A](fa: F[A]): A } -object Extract { +object Extract extends Extract0 { + // Ideally this would be an exported subclass instance provided by Comonad implicit def comonadIsExtract[F[_]](implicit ev: Comonad[F]): Extract[F] = new Extract[F] { def extract[A](fa: F[A]): A = ev.extract(fa) } + // Ideally this would be an instance exported to Comonad implicit def extractCoflatMapIsComonad[F[_]](implicit e: Extract[F], cf: CoflatMap[F]): Comonad[F] = new Comonad[F] { def extract[A](fa: F[A]): A = e.extract(fa) @@ -20,3 +23,6 @@ object Extract { def coflatMap[A, B](fa: F[A])(f: F[A] => B): F[B] = cf.coflatMap(fa)(f) } } + +@imports[Extract] +trait Extract0 diff --git a/core/src/main/scala/alleycats/One.scala b/core/src/main/scala/alleycats/One.scala index 1072a6f..f96a4e7 100644 --- a/core/src/main/scala/alleycats/One.scala +++ b/core/src/main/scala/alleycats/One.scala @@ -3,6 +3,7 @@ package alleycats import algebra.ring.{MultiplicativeMonoid, MultiplicativeSemigroup} import cats.{Eq, Monoid} import cats.syntax.eq._ +import export.imports import simulacrum.typeclass import scala.collection.generic.CanBuildFrom @@ -16,16 +17,21 @@ import scala.collection.generic.CanBuildFrom one =!= a } -object One { +object One extends One0 { def apply[A](a: => A): One[A] = new One[A] { lazy val one: A = a } + // Ideally this would be an exported subclass instance provided by MultiplicativeMonoid implicit def multiplicativeMonoidIsOne[A](implicit ev: MultiplicativeMonoid[A]): One[A] = One(ev.one) + // Ideally this would be an instance exported to MultiplicativeMonoid implicit def oneWithSemigroupIsMonoid[A](implicit z: One[A], s: MultiplicativeSemigroup[A]): MultiplicativeMonoid[A] = new MultiplicativeMonoid[A] { def one: A = z.one def times(x: A, y: A): A = s.times(x, y) } } + +@imports[One] +trait One0 diff --git a/core/src/main/scala/alleycats/Pure.scala b/core/src/main/scala/alleycats/Pure.scala index 367c9b6..efa3209 100644 --- a/core/src/main/scala/alleycats/Pure.scala +++ b/core/src/main/scala/alleycats/Pure.scala @@ -1,18 +1,21 @@ package alleycats import cats.{Applicative, FlatMap, Monad} +import export.imports import simulacrum.typeclass @typeclass trait Pure[F[_]] { def pure[A](a: A): F[A] } -object Pure { +object Pure extends Pure0 { + // Ideally this would be an exported subclass instance provided by Applicative implicit def applicativeIsPure[F[_]](implicit ev: Applicative[F]): Pure[F] = new Pure[F] { def pure[A](a: A): F[A] = ev.pure(a) } + // Ideally this would be an instance exported to Monad implicit def pureFlatMapIsMonad[F[_]](implicit p: Pure[F], fm: FlatMap[F]): Monad[F] = new Monad[F] { def pure[A](a: A): F[A] = p.pure(a) @@ -20,3 +23,6 @@ object Pure { def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = fm.flatMap(fa)(f) } } + +@imports[Pure] +trait Pure0 diff --git a/core/src/main/scala/alleycats/Zero.scala b/core/src/main/scala/alleycats/Zero.scala index 0a3ac38..de3044c 100644 --- a/core/src/main/scala/alleycats/Zero.scala +++ b/core/src/main/scala/alleycats/Zero.scala @@ -3,6 +3,7 @@ package alleycats import algebra.ring.{AdditiveMonoid, AdditiveSemigroup} import cats.{Eq, Monoid} import cats.syntax.eq._ +import export.imports import simulacrum.typeclass import scala.collection.generic.CanBuildFrom @@ -16,16 +17,21 @@ import scala.collection.generic.CanBuildFrom zero =!= a } -object Zero { +object Zero extends Zero0 { def apply[A](a: => A): Zero[A] = new Zero[A] { lazy val zero: A = a } + // Ideally this would be an exported subclass instance provided by AdditiveMonoid implicit def additiveMonoidIsZero[A](implicit ev: AdditiveMonoid[A]): Zero[A] = Zero(ev.zero) + // Ideally this would be an instance exported to AdditiveMonoid implicit def zeroWithSemigroupIsMonoid[A](implicit z: Zero[A], s: AdditiveSemigroup[A]): AdditiveMonoid[A] = new AdditiveMonoid[A] { def zero: A = z.zero def plus(x: A, y: A): A = s.plus(x, y) } } + +@imports[Zero] +trait Zero0 diff --git a/core/src/main/scala/alleycats/std/all.scala b/core/src/main/scala/alleycats/std/all.scala index fb08021..f26cfe6 100644 --- a/core/src/main/scala/alleycats/std/all.scala +++ b/core/src/main/scala/alleycats/std/all.scala @@ -1,7 +1,12 @@ -package alleycats.std +package alleycats +package std -object all - extends ListInstances - with OptionInstances - with SetInstances - with TryInstances +import export._ + +@reexports( + EmptyKInstances, + ListInstances, + OptionInstances, + SetInstances, + TryInstances +) object all extends LegacySetInstances with LegacyTryInstances diff --git a/core/src/main/scala/alleycats/std/list.scala b/core/src/main/scala/alleycats/std/list.scala index 1ba9d61..740278c 100644 --- a/core/src/main/scala/alleycats/std/list.scala +++ b/core/src/main/scala/alleycats/std/list.scala @@ -1,11 +1,22 @@ package alleycats package std -object list extends ListInstances +import export._ -trait ListInstances { - implicit val listEmptyK: EmptyK[List] = +@reexports(ListInstances) +object list + +@exports +object ListInstances { + @export(Orphan) + implicit val exportListEmptyK: EmptyK[List] = new EmptyK[List] { def empty[A]: List[A] = Nil } + + @export(Orphan) + implicit val exportListConsK: ConsK[List] = + new ConsK[List] { + def cons[A](hd: A, tl: List[A]): List[A] = hd :: tl + } } diff --git a/core/src/main/scala/alleycats/std/option.scala b/core/src/main/scala/alleycats/std/option.scala index 54e5d64..25a1cfe 100644 --- a/core/src/main/scala/alleycats/std/option.scala +++ b/core/src/main/scala/alleycats/std/option.scala @@ -1,10 +1,15 @@ package alleycats package std -object option extends OptionInstances +import export._ -trait OptionInstances { - implicit val optionEmptyK: EmptyK[Option] = +@reexports(OptionInstances) +object option + +@exports +object OptionInstances { + @export(Orphan) + implicit val exportOptionEmptyK: EmptyK[Option] = new EmptyK[Option] { def empty[A]: Option[A] = None } diff --git a/core/src/main/scala/alleycats/std/set.scala b/core/src/main/scala/alleycats/std/set.scala index 67c10d6..2c9354f 100644 --- a/core/src/main/scala/alleycats/std/set.scala +++ b/core/src/main/scala/alleycats/std/set.scala @@ -1,11 +1,10 @@ package alleycats.std import cats.{Applicative, Eval, Foldable, Monad, Traverse} +import export._ -object set extends SetInstances - -trait SetInstances { - +@exports +object SetInstances { // This method advertises parametricity, but relies on using // universal hash codes and equality, which hurts our ability to // rely on free theorems. @@ -26,6 +25,7 @@ trait SetInstances { // contain three. Since `g` is not a function (speaking strictly) // this would not be considered a law violation, but it still makes // people uncomfortable. + @export(Orphan) implicit val setMonad: Monad[Set] = new Monad[Set] { def pure[A](a: A): Set[A] = Set(a) @@ -36,6 +36,7 @@ trait SetInstances { // Since iteration order is not guaranteed for sets, folds and other // traversals may produce different results for input sets which // appear to be the same. + @export(Orphan) implicit val setTraverse: Traverse[Set] = new Traverse[Set] { def foldLeft[A, B](fa: Set[A], b: B)(f: (B, A) => B): B = @@ -50,3 +51,13 @@ trait SetInstances { } } } + +@reexports(SetInstances) +object set extends LegacySetInstances + +// TODO: remove when cats.{ Set, Traverse } support export-hook +trait LegacySetInstances { + implicit def legacySetMonad(implicit e: ExportOrphan[Monad[Set]]): Monad[Set] = e.instance + + implicit def legacySetTraverse(implicit e: ExportOrphan[Traverse[Set]]): Traverse[Set] = e.instance +} diff --git a/core/src/main/scala/alleycats/std/try.scala b/core/src/main/scala/alleycats/std/try.scala index 9c0c8ff..6611dc5 100644 --- a/core/src/main/scala/alleycats/std/try.scala +++ b/core/src/main/scala/alleycats/std/try.scala @@ -2,12 +2,14 @@ package alleycats package std import cats.Bimonad +import export._ import scala.util.Try -object try_ extends TryInstances - -trait TryInstances { +@reexports(TryInstances) +object try_ extends LegacyTryInstances +@exports +object TryInstances { // There are various concerns people have over Try's ability to // satisfy the monad laws. For example, consider the following code: // @@ -28,6 +30,8 @@ trait TryInstances { // Furthermore, since Cats has introduced a Bimonad[A], the Monad[Try] // and Comanad[Try] instances have been replaced by a single Bimonad[Try] // instance. + // + @export(Orphan) implicit val tryBimonad: Bimonad[Try] = new Bimonad[Try] { def pure[A](a: A): Try[A] = Try(a) @@ -37,3 +41,8 @@ trait TryInstances { def extract[A](p: Try[A]): A = p.get } } + +// TODO: remove when cats.{ Monad, Comonad, Bimonad } support export-hook +trait LegacyTryInstances { + implicit def legacyTryBimonad(implicit e: ExportOrphan[Bimonad[Try]]): Bimonad[Try] = e.instance +} From 2060df07c0f55e09ac35d23abf623f89c359a20c Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Tue, 10 Nov 2015 13:04:29 +0000 Subject: [PATCH 2/2] Bumped macro-compat and simulacrum versions. --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index c458839..862d103 100644 --- a/build.sbt +++ b/build.sbt @@ -30,12 +30,12 @@ val disciplineVersion = "0.4" val exportHookVersion = "1.0.3-SNAPSHOT" val kindProjectorVersion = "0.6.3" val machinistVersion = "0.4.1" -val macroCompatVersion = "1.0.2" +val macroCompatVersion = "1.1.0" val paradiseVersion = "2.1.0-M5" val scalacheckVersion = "1.12.4" val scalatestVersion = "3.0.0-M7" val scalacVersion = "2.11.7" -val simulacrumVersion= "0.4.0" +val simulacrumVersion= "0.5.0-SNAPSHOT" lazy val buildSettings = Seq( organization := "org.typelevel",