From d46e7168670731f25484f5e8716e26839b9e99ae Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Wed, 30 Sep 2015 10:49:47 +0100 Subject: [PATCH 1/6] Added support for export-hook; added ConsK. --- 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 +++++++++++--- 12 files changed, 128 insertions(+), 24 deletions(-) create mode 100644 core/src/main/scala/alleycats/ConsK.scala 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 17fd3143192d87d76eb2c5eece8fade7c7681ba9 Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Tue, 10 Nov 2015 14:08:33 +0000 Subject: [PATCH 2/6] Updates for sbt-catalysts. --- build.sbt | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index 11a6725..e45c77f 100644 --- a/build.sbt +++ b/build.sbt @@ -16,7 +16,14 @@ addCommandAlias("gitSnapshots", ";set version in ThisBuild := git.gitDescribedVe val gh = GitHubSettings(org = "non", proj = "alleycats", publishOrg = "org.typelevel", license = mit) val devs = Seq(Dev("Erik Osheim", "non")) -val vers = typelevel.versions ++ alleycats.versions +val updates = Map( + "macro-compat" -> "1.1.0", + "export-hook" -> "1.0.3-SNAPSHOT", + "simulacrum" -> "0.5.0-SNAPSHOT" +) +val updatesSettings = Seq( resolvers += Resolver.sonatypeRepo("snapshots")) + +val vers = typelevel.versions ++ alleycats.versions ++ updates val libs = typelevel.libraries ++ alleycats.libraries val addins = typelevel.scalacPlugins ++ alleycats.scalacPlugins val vAll = Versions(vers, libs, addins) @@ -24,7 +31,7 @@ val vAll = Versions(vers, libs, addins) /** * alleycats - This is the root project that aggregates the alleycatsJVM and alleycatsJS sub projects */ -lazy val rootSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings +lazy val rootSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings ++ updatesSettings lazy val module = mkModuleFactory(gh.proj, mkConfig(rootSettings, commonJvmSettings, commonJsSettings)) lazy val prj = mkPrjFactory(rootSettings) @@ -51,7 +58,7 @@ lazy val coreJVM = coreM.jvm lazy val coreJS = coreM.js lazy val coreM = module("core", CrossType.Pure) .settings(typelevel.macroCompatSettings(vAll):_*) - .settings(addLibs(vAll, "cats-core"):_*) + .settings(addLibs(vAll, "cats-core", "export-hook"):_*) /** * Laws project @@ -86,7 +93,7 @@ lazy val commonSettings = sharedCommonSettings ++ addCompilerPlugins(vAll, "kind-projector") ++ Seq( scalacOptions ++= scalacAllOptions, parallelExecution in Test := false -) ++ warnUnusedImport ++ unidocCommonSettings +) /* ++ warnUnusedImport */ ++ unidocCommonSettings // spurious warnings from macro annotations expected lazy val commonJsSettings = Seq( scalaJSStage in Global := FastOptStage From c8cd485f3829d5d5dc3bcc7dab1d06d430123aff Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Thu, 12 Nov 2015 08:17:32 +0000 Subject: [PATCH 3/6] Use updated simulacrum in core. --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index e45c77f..dce60e8 100644 --- a/build.sbt +++ b/build.sbt @@ -18,7 +18,7 @@ val devs = Seq(Dev("Erik Osheim", "non")) val updates = Map( "macro-compat" -> "1.1.0", - "export-hook" -> "1.0.3-SNAPSHOT", + "export-hook" -> "1.1.0", "simulacrum" -> "0.5.0-SNAPSHOT" ) val updatesSettings = Seq( resolvers += Resolver.sonatypeRepo("snapshots")) @@ -58,7 +58,7 @@ lazy val coreJVM = coreM.jvm lazy val coreJS = coreM.js lazy val coreM = module("core", CrossType.Pure) .settings(typelevel.macroCompatSettings(vAll):_*) - .settings(addLibs(vAll, "cats-core", "export-hook"):_*) + .settings(addLibs(vAll, "cats-core", "export-hook", "simulacrum"):_*) /** * Laws project From ccb46936b857b755d148f463bc4f94016fb41c8e Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Thu, 12 Nov 2015 08:18:26 +0000 Subject: [PATCH 4/6] Remove workaround for simulacrum bug. --- core/src/main/scala/alleycats/EmptyK.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/src/main/scala/alleycats/EmptyK.scala b/core/src/main/scala/alleycats/EmptyK.scala index f9ebafd..85596d3 100644 --- a/core/src/main/scala/alleycats/EmptyK.scala +++ b/core/src/main/scala/alleycats/EmptyK.scala @@ -12,12 +12,8 @@ import simulacrum.typeclass } } -object EmptyK extends EmptyK0 - -// Currently a simulacrum bug prevents this trait from being folded into -// the object @imports[EmptyK] -trait EmptyK0 +object EmptyK @exports object EmptyKInstances { From 5feb09afdd6e4fa9e421a64f04d23a3d3c127516 Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Thu, 12 Nov 2015 13:40:42 +0000 Subject: [PATCH 5/6] Bumped simulacrum to 0.5.0. --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index dce60e8..2a9020a 100644 --- a/build.sbt +++ b/build.sbt @@ -19,7 +19,7 @@ val devs = Seq(Dev("Erik Osheim", "non")) val updates = Map( "macro-compat" -> "1.1.0", "export-hook" -> "1.1.0", - "simulacrum" -> "0.5.0-SNAPSHOT" + "simulacrum" -> "0.5.0" ) val updatesSettings = Seq( resolvers += Resolver.sonatypeRepo("snapshots")) From 3a29c53edc97e479d5124794e868b5f948987a44 Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Mon, 16 Nov 2015 11:25:31 +0000 Subject: [PATCH 6/6] Removed Sonatype snapshots repo. --- build.sbt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 2a9020a..e287b11 100644 --- a/build.sbt +++ b/build.sbt @@ -21,7 +21,6 @@ val updates = Map( "export-hook" -> "1.1.0", "simulacrum" -> "0.5.0" ) -val updatesSettings = Seq( resolvers += Resolver.sonatypeRepo("snapshots")) val vers = typelevel.versions ++ alleycats.versions ++ updates val libs = typelevel.libraries ++ alleycats.libraries @@ -31,7 +30,7 @@ val vAll = Versions(vers, libs, addins) /** * alleycats - This is the root project that aggregates the alleycatsJVM and alleycatsJS sub projects */ -lazy val rootSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings ++ updatesSettings +lazy val rootSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings lazy val module = mkModuleFactory(gh.proj, mkConfig(rootSettings, commonJvmSettings, commonJsSettings)) lazy val prj = mkPrjFactory(rootSettings)