Skip to content

Commit

Permalink
Merge pull request #22 from milessabin/master
Browse files Browse the repository at this point in the history
Initial support for export-hook
  • Loading branch information
ceedubs committed Nov 17, 2015
2 parents f71f240 + 3a29c53 commit 44181bf
Show file tree
Hide file tree
Showing 13 changed files with 133 additions and 27 deletions.
12 changes: 9 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ 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.1.0",
"simulacrum" -> "0.5.0"
)

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)
Expand Down Expand Up @@ -51,7 +57,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", "simulacrum"):_*)

/**
* Laws project
Expand Down Expand Up @@ -86,7 +92,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
Expand Down
20 changes: 20 additions & 0 deletions core/src/main/scala/alleycats/ConsK.scala
Original file line number Diff line number Diff line change
@@ -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

7 changes: 6 additions & 1 deletion core/src/main/scala/alleycats/Empty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
10 changes: 10 additions & 0 deletions core/src/main/scala/alleycats/EmptyK.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package alleycats

import export._
import simulacrum.typeclass

@typeclass trait EmptyK[F[_]] { self =>
Expand All @@ -10,3 +11,12 @@ import simulacrum.typeclass
def empty: F[A] = self.empty[A]
}
}

@imports[EmptyK]
object EmptyK

@exports
object EmptyKInstances {
@export(Instantiated)
implicit def instantiate[F[_], T](implicit ekf: EmptyK[F]): Empty[F[T]] = ekf.synthesize[T]
}
8 changes: 7 additions & 1 deletion core/src/main/scala/alleycats/Extract.scala
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
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)
override def map[A, B](fa: F[A])(f: A => B): F[B] = cf.map(fa)(f)
def coflatMap[A, B](fa: F[A])(f: F[A] => B): F[B] = cf.coflatMap(fa)(f)
}
}

@imports[Extract]
trait Extract0
8 changes: 7 additions & 1 deletion core/src/main/scala/alleycats/One.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
8 changes: 7 additions & 1 deletion core/src/main/scala/alleycats/Pure.scala
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
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)
override def map[A, B](fa: F[A])(f: A => B): F[B] = fm.map(fa)(f)
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = fm.flatMap(fa)(f)
}
}

@imports[Pure]
trait Pure0
8 changes: 7 additions & 1 deletion core/src/main/scala/alleycats/Zero.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
17 changes: 11 additions & 6 deletions core/src/main/scala/alleycats/std/all.scala
Original file line number Diff line number Diff line change
@@ -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
17 changes: 14 additions & 3 deletions core/src/main/scala/alleycats/std/list.scala
Original file line number Diff line number Diff line change
@@ -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
}
}
11 changes: 8 additions & 3 deletions core/src/main/scala/alleycats/std/option.scala
Original file line number Diff line number Diff line change
@@ -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
}
Expand Down
19 changes: 15 additions & 4 deletions core/src/main/scala/alleycats/std/set.scala
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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)
Expand All @@ -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 =
Expand All @@ -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
}
15 changes: 12 additions & 3 deletions core/src/main/scala/alleycats/std/try.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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:
//
Expand All @@ -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)
Expand All @@ -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
}

0 comments on commit 44181bf

Please sign in to comment.