Skip to content

Commit

Permalink
Merge pull request #806 from rklaehn/topic/kernel
Browse files Browse the repository at this point in the history
Use simulacrum for typeclass boilerplate
  • Loading branch information
ceedubs committed Jan 29, 2016
2 parents e7affc9 + 87f2f3c commit 82c436f
Show file tree
Hide file tree
Showing 46 changed files with 596 additions and 253 deletions.
21 changes: 20 additions & 1 deletion AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,47 +14,66 @@ possible:

* Adelbert Chang
* Alessandro Lacava
* Alexey Levan
* Alissa Pajer
* Alistair Johnson
* Amir Mohammad Saied
* Andrew Jones
* Antoine Comte
* Arya Irani
* Ash Pook
* Benjamin Thuillier
* Bobby
* Bobby Rauchenberg
* Brendan McAdams
* Cody Allen
* Colt Frederickson
* Dale Wijnand
* Dave Rostron
* David Allsopp
* David Gregory
* Denis Mikhaylov
* Derek Wickern
* Edmund Noble
* Erik LaBianca
* Erik Osheim
* Eugene Burmako
* Eugene Yokota
* Feynman Liang
* Frank S. Thomas
* Jean-Rémi Desjardins
* Jisoo Park
* Josh Marcus
* Julien Richard-Foy
* Julien Truffaut
* Kenji Yoshida
* Long Cao
* Luis Angel Vicente Sanchez
* Luke Wyman
* Marc Siegel
* Markus Hauck
* Matthias Lüneberg
* Michael Pilquist
* Mike Curry
* Miles Sabin
* Olli Helenius
* Owen Parry
* Pascal Voitot
* Paul Phillips
* Philip Wills
* Raúl Raja Martínez
* Rintcius Blok
* Rob Norris
* Romain Ruetschi
* Ross A. Baker
* Sarunas Valaskevicius
* Shunsuke Otani
* Sinisa Louc
* Stephen Judkins
* Stew O'Connor
* Sumedh Mungee
* Travis Brown
* Wedens
* Yosef Fertel
* Zach Abbott

We've tried to include everyone, but if you've made a contribution to
Expand Down
24 changes: 19 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,18 @@ lazy val commonSettings = Seq(
Resolver.sonatypeRepo("snapshots")
),
libraryDependencies ++= Seq(
"com.github.mpilquist" %%% "simulacrum" % "0.5.0",
"org.typelevel" %%% "machinist" % "0.4.1",
"com.github.mpilquist" %%% "simulacrum" % "0.6.1" % "provided",
compilerPlugin("org.scalamacros" %% "paradise" % "2.1.0-M5" cross CrossVersion.full),
compilerPlugin("org.spire-math" %% "kind-projector" % "0.6.3")
),
parallelExecution in Test := false,
scalacOptions in (Compile, doc) := (scalacOptions in (Compile, doc)).value.filter(_ != "-Xfatal-warnings")
) ++ warnUnusedImport

lazy val machinistDependencies = Seq(
libraryDependencies += "org.typelevel" %%% "machinist" % "0.4.1"
)

lazy val commonJsSettings = Seq(
scalaJSStage in Global := FastOptStage,
parallelExecution := false
Expand All @@ -56,7 +59,9 @@ lazy val commonJvmSettings = Seq(
// JVM settings. https://github.com/tkawachi/sbt-doctest/issues/52
) ++ catsDoctestSettings

lazy val catsSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings
lazy val kernelSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings

lazy val catsSettings = kernelSettings ++ machinistDependencies

lazy val scalacheckVersion = "1.12.5"

Expand Down Expand Up @@ -128,15 +133,24 @@ lazy val macros = crossProject.crossType(CrossType.Pure)
lazy val macrosJVM = macros.jvm
lazy val macrosJS = macros.js


lazy val kernel = crossProject.crossType(CrossType.Pure)
.settings(moduleName := "cats-kernel")
.settings(catsSettings:_*)
.settings(kernelSettings:_*)
.settings(mimaDefaultSettings:_*)
.settings(previousArtifacts := Set(
// TODO: Add cats-kernel artifacts as they are released, e.g.
// "org.spire-math" %% "cats-kernel" % "0.4.0"
))
.settings(pomPostProcess := { (node) =>
import scala.xml._
import scala.xml.transform._
def stripIf(f: Node => Boolean) = new RewriteRule {
override def transform(n: Node) =
if (f(n)) NodeSeq.Empty else n
}
val stripProvidedScope = stripIf { n => n.label == "dependency" && (n \ "scope").text == "provided" }
new RuleTransformer(stripProvidedScope).transform(node)(0)
})
.jsSettings(commonJsSettings:_*)
.jvmSettings(commonJvmSettings:_*)

Expand Down
81 changes: 81 additions & 0 deletions core/src/main/scala/cats/ApplicativeError.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package cats

import cats.data.{Xor, XorT}

/**
* An applicative that also allows you to raise and or handle an error value.
*
* This type class allows one to abstract over error-handling applicatives.
*/
trait ApplicativeError[F[_], E] extends Applicative[F] {
/**
* Lift an error into the `F` context.
*/
def raiseError[A](e: E): F[A]

/**
* Handle any error, potentially recovering from it, by mapping it to an
* `F[A]` value.
*
* @see [[handleError]] to handle any error by simply mapping it to an `A`
* value instead of an `F[A]`.
*
* @see [[recoverWith]] to recover from only certain errors.
*/
def handleErrorWith[A](fa: F[A])(f: E => F[A]): F[A]

/**
* Handle any error, by mapping it to an `A` value.
*
* @see [[handleErrorWith]] to map to an `F[A]` value instead of simply an
* `A` value.
*
* @see [[recover]] to only recover from certain errors.
*/
def handleError[A](fa: F[A])(f: E => A): F[A] = handleErrorWith(fa)(f andThen pure)

/**
* Handle errors by turning them into [[cats.data.Xor.Left]] values.
*
* If there is no error, then an [[cats.data.Xor.Right]] value will be returned instead.
*
* All non-fatal errors should be handled by this method.
*/
def attempt[A](fa: F[A]): F[E Xor A] = handleErrorWith(
map(fa)(Xor.right[E, A])
)(e => pure(Xor.left(e)))

/**
* Similar to [[attempt]], but wraps the result in a [[cats.data.XorT]] for
* convenience.
*/
def attemptT[A](fa: F[A]): XorT[F, E, A] = XorT(attempt(fa))

/**
* Recover from certain errors by mapping them to an `A` value.
*
* @see [[handleError]] to handle any/all errors.
*
* @see [[recoverWith]] to recover from certain errors by mapping them to
* `F[A]` values.
*/
def recover[A](fa: F[A])(pf: PartialFunction[E, A]): F[A] =
handleErrorWith(fa)(e =>
(pf andThen pure) applyOrElse(e, raiseError))

/**
* Recover from certain errors by mapping them to an `F[A]` value.
*
* @see [[handleErrorWith]] to handle any/all errors.
*
* @see [[recover]] to recover from certain errors by mapping them to `A`
* values.
*/
def recoverWith[A](fa: F[A])(pf: PartialFunction[E, F[A]]): F[A] =
handleErrorWith(fa)(e =>
pf applyOrElse(e, raiseError))
}

object ApplicativeError {
def apply[F[_], E](implicit F: ApplicativeError[F, E]): ApplicativeError[F, E] = F
}
29 changes: 15 additions & 14 deletions core/src/main/scala/cats/Eval.scala
Original file line number Diff line number Diff line change
Expand Up @@ -223,20 +223,21 @@ object Eval extends EvalInstances {
*/
sealed abstract class Call[A](val thunk: () => Eval[A]) extends Eval[A] {
def memoize: Eval[A] = new Later(() => value)
def value: A = {
def loop(fa: Eval[A]): Eval[A] = fa match {
case call: Eval.Call[A] =>
loop(call.thunk())
case compute: Eval.Compute[A] =>
new Eval.Compute[A] {
type Start = compute.Start
val start: () => Eval[Start] = () => compute.start()
val run: Start => Eval[A] = s => loop(compute.run(s))
}
case other => other
}

loop(this).value
def value: A = Call.loop(this).value
}

object Call {
/** Collapse the call stack for eager evaluations */
private def loop[A](fa: Eval[A]): Eval[A] = fa match {
case call: Eval.Call[A] =>
loop(call.thunk())
case compute: Eval.Compute[A] =>
new Eval.Compute[A] {
type Start = compute.Start
val start: () => Eval[Start] = () => compute.start()
val run: Start => Eval[A] = s => loop(compute.run(s))
}
case other => other
}
}

Expand Down
51 changes: 49 additions & 2 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import simulacrum.typeclass
*
* See: [[http://www.cs.nott.ac.uk/~pszgmh/fold.pdf A tutorial on the universality and expressiveness of fold]]
*/
@typeclass trait Foldable[F[_]] extends Serializable { self =>
@typeclass trait Foldable[F[_]] { self =>

/**
* Left associative fold on 'F' using the function 'f'.
Expand Down Expand Up @@ -107,6 +107,31 @@ import simulacrum.typeclass
G.map2(acc, f(a)) { (_, _) => () }
}

/**
* Behaves like traverse_, but uses [[Unapply]] to find the
* [[Applicative]] instance for `G` - used when `G` is a
* type constructor with two or more parameters such as [[cats.data.Xor]]
*
* {{{
* scala> import cats.data.Xor
* scala> import cats.std.list._
* scala> def parseInt(s: String): Xor[String, Int] =
* | try { Xor.Right(s.toInt) }
* | catch { case _: NumberFormatException => Xor.Left("boo") }
* scala> val F = Foldable[List]
* scala> F.traverseU_(List("333", "444"))(parseInt)
* res0: Xor[String, Unit] = Right(())
* scala> F.traverseU_(List("333", "zzz"))(parseInt)
* res1: Xor[String, Unit] = Left(boo)
* }}}
*
* Note that using `traverse_` instead of `traverseU_` would not compile without
* explicitly passing in the type parameters - the type checker has trouble
* inferring the appropriate instance.
*/
def traverseU_[A, GB](fa: F[A])(f: A => GB)(implicit U: Unapply[Applicative, GB]): U.M[Unit] =
traverse_(fa)(f.andThen(U.subst))(U.TC)

/**
* Sequence `F[G[A]]` using `Applicative[G]`.
*
Expand All @@ -125,9 +150,31 @@ import simulacrum.typeclass
* res1: Option[Unit] = None
* }}}
*/
def sequence_[G[_]: Applicative, A, B](fga: F[G[A]]): G[Unit] =
def sequence_[G[_]: Applicative, A](fga: F[G[A]]): G[Unit] =
traverse_(fga)(identity)

/**
* Behaves like sequence_, but uses [[Unapply]] to find the
* [[Applicative]] instance for `G` - used when `G` is a
* type constructor with two or more parameters such as [[cats.data.Xor]]
*
* {{{
* scala> import cats.data.Xor
* scala> import cats.std.list._
* scala> val F = Foldable[List]
* scala> F.sequenceU_(List(Xor.right[String, Int](333), Xor.Right(444)))
* res0: Xor[String, Unit] = Right(())
* scala> F.sequenceU_(List(Xor.right[String, Int](333), Xor.Left("boo")))
* res1: Xor[String, Unit] = Left(boo)
* }}}
*
* Note that using `sequence_` instead of `sequenceU_` would not compile without
* explicitly passing in the type parameters - the type checker has trouble
* inferring the appropriate instance.
*/
def sequenceU_[GA](fa: F[GA])(implicit U: Unapply[Applicative, GA]): U.M[Unit] =
traverseU_(fa)(identity)

/**
* Fold implemented using the given `MonoidK[G]` instance.
*
Expand Down
69 changes: 1 addition & 68 deletions core/src/main/scala/cats/MonadError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,74 +7,7 @@ import cats.data.{Xor, XorT}
*
* This type class allows one to abstract over error-handling monads.
*/
trait MonadError[F[_], E] extends Monad[F] {
/**
* Lift an error into the `F` context.
*/
def raiseError[A](e: E): F[A]

/**
* Handle any error, potentially recovering from it, by mapping it to an
* `F[A]` value.
*
* @see [[handleError]] to handle any error by simply mapping it to an `A`
* value instead of an `F[A]`.
*
* @see [[recoverWith]] to recover from only certain errors.
*/
def handleErrorWith[A](fa: F[A])(f: E => F[A]): F[A]

/**
* Handle any error, by mapping it to an `A` value.
*
* @see [[handleErrorWith]] to map to an `F[A]` value instead of simply an
* `A` value.
*
* @see [[recover]] to only recover from certain errors.
*/
def handleError[A](fa: F[A])(f: E => A): F[A] = handleErrorWith(fa)(f andThen pure)

/**
* Handle errors by turning them into [[cats.data.Xor.Left]] values.
*
* If there is no error, then an [[cats.data.Xor.Right]] value will be returned instead.
*
* All non-fatal errors should be handled by this method.
*/
def attempt[A](fa: F[A]): F[E Xor A] = handleErrorWith(
map(fa)(Xor.right[E, A])
)(e => pure(Xor.left(e)))

/**
* Similar to [[attempt]], but wraps the result in a [[cats.data.XorT]] for
* convenience.
*/
def attemptT[A](fa: F[A]): XorT[F, E, A] = XorT(attempt(fa))

/**
* Recover from certain errors by mapping them to an `A` value.
*
* @see [[handleError]] to handle any/all errors.
*
* @see [[recoverWith]] to recover from certain errors by mapping them to
* `F[A]` values.
*/
def recover[A](fa: F[A])(pf: PartialFunction[E, A]): F[A] =
handleErrorWith(fa)(e =>
(pf andThen pure) applyOrElse(e, raiseError))

/**
* Recover from certain errors by mapping them to an `F[A]` value.
*
* @see [[handleErrorWith]] to handle any/all errors.
*
* @see [[recover]] to recover from certain errors by mapping them to `A`
* values.
*/
def recoverWith[A](fa: F[A])(pf: PartialFunction[E, F[A]]): F[A] =
handleErrorWith(fa)(e =>
pf applyOrElse(e, raiseError))
}
trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F]

object MonadError {
def apply[F[_], E](implicit F: MonadError[F, E]): MonadError[F, E] = F
Expand Down
Loading

0 comments on commit 82c436f

Please sign in to comment.