Skip to content

Commit

Permalink
Merge branch 'master' into feature/translift-expression
Browse files Browse the repository at this point in the history
  • Loading branch information
djspiewak committed Apr 2, 2016
2 parents 4226c65 + 7e49a85 commit 2dc050d
Show file tree
Hide file tree
Showing 31 changed files with 422 additions and 29 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ project/boot
target
.ensime
.ensime_lucene
.ensime_cache
TAGS
\#*#
*~
Expand Down
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ scala:
script:
- scripts/travis-publish.sh

# http://austinpray.com/ops/2015/09/20/change-travis-node-version.html
install:
- rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION

notifications:
webhooks:
urls:
Expand All @@ -21,12 +25,16 @@ env:
global:
- secure: Kf44XQFpq2QGe3rn98Dsf5Uz3WXzPDralS54co7sqT5oQGs1mYLYZRYz+I75ZSo5ffZ86H7M+AI9YFofqGwAjBixBbqf1tGkUh3oZp2fN3QfqzazGV3HzC+o41zALG5FL+UBaURev9ChQ5fYeTtFB7YAzejHz4y5E97awk934Rg=
- secure: QbNAu0jCaKrwjJi7KZtYEBA/pYbTJ91Y1x/eLAJpsamswVOvwnThA/TLYuux+oiZQCiDUpBzP3oxksIrEEUAhl0lMtqRFY3MrcUr+si9NIjX8hmoFwkvZ5o1b7pmLF6Vz3rQeP/EWMLcljLzEwsrRXeK0Ei2E4vFpsg8yz1YXJg=
- TRAVIS_NODE_VERSION="4"
- CATS_BOT_BUILD=true
cache:
directories:
- $HOME/.sbt/0.13/dependency
- $HOME/.sbt/boot/scala*
- $HOME/.sbt/launchers
- $HOME/.ivy2/cache
- $HOME/.nvm

before_cache:
- du -h -d 1 $HOME/.ivy2/cache
- du -h -d 2 $HOME/.sbt/
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@

### Overview

Cats is a proof-of-concept library intended to provide abstractions
for functional programming in Scala.
Cats is a library which provides abstractions for functional programming in Scala.

The name is a playful shortening of the word *category*.

Expand Down
32 changes: 30 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import sbtunidoc.Plugin.UnidocKeys._
import ReleaseTransformations._
import ScoverageSbtPlugin._

lazy val botBuild = settingKey[Boolean]("Build by TravisCI instead of local dev environment")

lazy val scoverageSettings = Seq(
ScoverageKeys.coverageMinimum := 60,
ScoverageKeys.coverageFailOnMinimum := false,
Expand Down Expand Up @@ -55,7 +57,15 @@ lazy val commonJsSettings = Seq(
s"-P:scalajs:mapSourceURI:$a->$g/"
},
scalaJSStage in Global := FastOptStage,
parallelExecution := false
parallelExecution := false,
// Using Rhino as jsEnv to build scala.js code can lead to OOM, switch to PhantomJS by default
scalaJSUseRhino := false,
requiresDOM := false,
jsEnv := NodeJSEnv().value,
// Only used for scala.js for now
botBuild := sys.props.getOrElse("CATS_BOT_BUILD", default="false") == "true",
// batch mode decreases the amount of memory needed to compile scala.js code
scalaJSOptimizerOptions := scalaJSOptimizerOptions.value.withBatchMode(botBuild.value)
)

lazy val commonJvmSettings = Seq(
Expand All @@ -73,9 +83,21 @@ lazy val disciplineDependencies = Seq(
libraryDependencies += "org.typelevel" %%% "discipline" % "0.4"
)

/**
* Remove 2.10 projects from doc generation, as the macros used in the projects
* cause problems generating the documentation on scala 2.10. As the APIs for 2.10
* and 2.11 are the same this has no effect on the resultant documentation, though
* it does mean that the scaladocs cannot be generated when the build is in 2.10 mode.
*/
def noDocProjects(sv: String): Seq[ProjectReference] = CrossVersion.partialVersion(sv) match {
case Some((2, 10)) => Seq[ProjectReference](coreJVM)
case _ => Nil
}

lazy val docSettings = Seq(
autoAPIMappings := true,
unidocProjectFilter in (ScalaUnidoc, unidoc) := inProjects(coreJVM),
unidocProjectFilter in (ScalaUnidoc, unidoc) :=
inProjects(coreJVM) -- inProjects(noDocProjects(scalaVersion.value): _*),
site.addMappingsToSiteDir(mappings in (ScalaUnidoc, packageDoc), "api"),
site.addMappingsToSiteDir(tut, "_tut"),
ghpagesNoJekyll := false,
Expand Down Expand Up @@ -209,6 +231,12 @@ lazy val publishSettings = Seq(
scmInfo := Some(ScmInfo(url("https://github.com/typelevel/cats"), "scm:git:[email protected]:typelevel/cats.git")),
autoAPIMappings := true,
apiURL := Some(url("http://typelevel.org/cats/api/")),
publishArtifact in (Compile, packageDoc) := {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 10)) => false // don't package scaladoc when publishing for 2.10
case _ => true
}
},
pomExtra := (
<developers>
<developer>
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/Bitraverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ trait Bitraverse[F[_, _]] extends Bifoldable[F] with Bifunctor[F] { self =>
bitraverse(fab)(identity, identity)

/** If F and G are both [[cats.Bitraverse]] then so is their composition F[G[_, _], G[_, _]] */
def compose[G[_, _]](implicit ev: Bitraverse[G]): Bifoldable[Lambda[(A, B) => F[G[A, B], G[A, B]]]] =
def compose[G[_, _]](implicit ev: Bitraverse[G]): Bitraverse[Lambda[(A, B) => F[G[A, B], G[A, B]]]] =
new CompositeBitraverse[F, G] {
val F = self
val G = ev
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/Reducible.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ import simulacrum.typeclass
* that we only need `Apply[G]` here, since we don't need to call
* `Applicative#pure` for a starting value.
*/
def sequence1_[G[_], A, B](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] =
def sequence1_[G[_], A](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] =
G.map(reduceLeft(fga)((x, y) => G.map2(x, y)((_, b) => b)))(_ => ())

/**
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/scala/cats/data/OptionT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ object OptionT extends OptionTInstances {
def pure[F[_], A](a: A)(implicit F: Applicative[F]): OptionT[F, A] =
OptionT(F.pure(Some(a)))

/** An alias for pure */
def some[F[_], A](a: A)(implicit F: Applicative[F]): OptionT[F, A] =
pure(a)

def none[F[_], A](implicit F: Applicative[F]) : OptionT[F, A] =
OptionT(F.pure(None))

/**
* Transforms an `Option` into an `OptionT`, lifted into the specified `Applicative`.
*
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ package object cats {
* encodes pure unary function application.
*/
type Id[A] = A
implicit val Id: Bimonad[Id] with Traverse[Id] =
implicit val idInstances: Bimonad[Id] with Traverse[Id] =
new Bimonad[Id] with Traverse[Id] {
def pure[A](a: A): A = a
def extract[A](a: A): A = a
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package syntax

trait AllSyntax
extends ApplicativeSyntax
with ApplicativeErrorSyntax
with ApplySyntax
with BifunctorSyntax
with BifoldableSyntax
Expand Down
37 changes: 37 additions & 0 deletions core/src/main/scala/cats/syntax/applicativeError.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package cats
package syntax

import cats.data.{Xor, XorT}

trait ApplicativeErrorSyntax {
implicit def applicativeErrorIdSyntax[E](e: E): ApplicativeErrorIdOps[E] =
new ApplicativeErrorIdOps(e)

implicit def applicativeErrorSyntax[F[_, _], E, A](fa: F[E, A])(implicit F: ApplicativeError[F[E, ?], E]): ApplicativeErrorOps[F[E, ?], E, A] =
new ApplicativeErrorOps[F[E, ?], E, A](fa)
}

final class ApplicativeErrorIdOps[E](e: E) {
def raiseError[F[_], A](implicit F: ApplicativeError[F, E]): F[A] =
F.raiseError(e)
}

final class ApplicativeErrorOps[F[_], E, A](fa: F[A])(implicit F: ApplicativeError[F, E]) {
def handleError(f: E => A): F[A] =
F.handleError(fa)(f)

def handleErrorWith(f: E => F[A]): F[A] =
F.handleErrorWith(fa)(f)

def attempt: F[E Xor A] =
F.attempt(fa)

def attemptT: XorT[F, E, A] =
F.attemptT(fa)

def recover(pf: PartialFunction[E, A]): F[A] =
F.recover(fa)(pf)

def recoverWith(pf: PartialFunction[E, F[A]]): F[A] =
F.recoverWith(fa)(pf)
}
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cats
package object syntax {
object all extends AllSyntax
object applicative extends ApplicativeSyntax
object applicativeError extends ApplicativeErrorSyntax
object apply extends ApplySyntax
object bifunctor extends BifunctorSyntax
object bifoldable extends BifoldableSyntax
Expand Down
17 changes: 17 additions & 0 deletions docs/src/main/tut/optiont.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,23 @@ val result: Future[Option[String]] = ot.value // Future(Some("Hello Jane Doe"))
```

## From `A` to `OptionT[F,A]`

If you have only an `A` and you wish to *lift* it into an `OptionT[F,A]` assuming you have an [`Applicative`]({{ site.baseurl }}/tut/applicative.html) instance for `F` you can use `some` which is an alias for `pure`. There also exists a `none` method which can be used to create an `OptionT[F,A]`, where the `Option` wrapped `A` type is actually a `None`:

```tut:silent
import cats.std.future._
val greet: OptionT[Future,String] = OptionT.pure("Hola!")
val greetAlt: OptionT[Future,String] = OptionT.some("Hi!")
val failedGreet: OptionT[Future,String] = OptionT.none
```


## Beyond map

Sometimes the operation you want to perform on an `Future[Option[String]]` might not be as simple as just wrapping the `Option` method in a `Future.map` call. For example, what if we want to greet the customer with their custom greeting if it exists but otherwise fall back to a default `Future[String]` greeting? Without `OptionT`, this implementation might look like:
Expand Down
19 changes: 9 additions & 10 deletions docs/src/site/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ layout: default
title: "Home"
section: "home"
---
Cats is an experimental library intended to provide abstractions for
functional programming in the
[Scala programming language](https://scala-lang.org). The name is a
playful shortening of the word *category*.

<div class="msg warn"> <p><strong> Cats is currently an experimental
project under active development</strong>. Feedback and
contributions are welcomed as we look to improve the project. This
project is evolving quickly and we currently make no guarantees about what
might drastically change in the near future.</p> </div>
Cats is a library which provides abstractions for functional
programming in the [Scala programming language](https://scala-lang.org).
The name is a playful shortening of the word *category*.

<div class="msg warn"> <p><strong> Cats is a new project under active
development</strong>. Feedback and contributions are welcomed as we look
to improve it. This project is evolving quickly and we are making no
guarantees about stability until a 1.0 release is made (current est.
around Q3 2016).</p> </div>


<a name="getting-started"></a>
Expand Down
7 changes: 7 additions & 0 deletions jvm/src/test/scala/cats/tests/FutureTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package tests

import cats.data.Xor
import cats.laws.discipline._
import cats.laws.discipline.arbitrary.evalArbitrary
import cats.laws.discipline.eq.tuple3Eq
import cats.jvm.std.future.futureComonad
import cats.tests.CatsSuite
Expand Down Expand Up @@ -40,4 +41,10 @@ class FutureTests extends CatsSuite {

checkAll("Future[Int]", MonadErrorTests[Future, Throwable].monadError[Int, Int, Int])
checkAll("Future[Int]", ComonadTests[Future].comonad[Int, Int, Int])

test("pureEval lifts a potentially lazy value into Future") {
forAll { e: Eval[Int] =>
e.pureEval[Future].extract should === (e.value)
}
}
}
6 changes: 6 additions & 0 deletions laws/src/main/scala/cats/laws/ReducibleLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ trait ReducibleLaws[F[_]] extends FoldableLaws[F] {
B: Semigroup[B]
): IsEq[B] =
fa.reduceMap(f) <-> fa.reduceRightTo(f)((a, eb) => eb.map(f(a) |+| _)).value

def traverseConsistent[G[_]: Applicative, A, B](fa: F[A], f: A => G[B]): IsEq[G[Unit]] =
fa.traverse1_(f) <-> fa.traverse_(f)

def sequenceConsistent[G[_]: Applicative, A](fa: F[G[A]]): IsEq[G[Unit]] =
fa.sequence1_ <-> fa.sequence_
}

object ReducibleLaws {
Expand Down
7 changes: 7 additions & 0 deletions laws/src/main/scala/cats/laws/discipline/Eq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package laws
package discipline

import algebra.Eq
import cats.std.string._
import org.scalacheck.Arbitrary

object eq {
Expand All @@ -21,6 +22,12 @@ object eq {
}
}

/** Create an approximation of Eq[Show[A]] by using function1Eq[A, String] */
implicit def showEq[A: Arbitrary]: Eq[Show[A]] =
Eq.by[Show[A], A => String] { showInstance =>
(a: A) => showInstance.show(a)
}

// Temporary, see https://github.com/non/algebra/pull/82
implicit def tuple2Eq[A, B](implicit A: Eq[A], B: Eq[B]): Eq[(A, B)] =
new Eq[(A, B)] {
Expand Down
9 changes: 7 additions & 2 deletions laws/src/main/scala/cats/laws/discipline/ReducibleTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@ import org.scalacheck.Prop.forAll
trait ReducibleTests[F[_]] extends FoldableTests[F] {
def laws: ReducibleLaws[F]

def reducible[A: Arbitrary, B: Arbitrary](implicit
def reducible[G[_]: Applicative, A: Arbitrary, B: Arbitrary](implicit
ArbFA: Arbitrary[F[A]],
ArbFGA: Arbitrary[F[G[A]]],
ArbGB: Arbitrary[G[B]],
B: Monoid[B],
EqG: Eq[G[Unit]],
EqB: Eq[B]
): RuleSet =
new DefaultRuleSet(
name = "reducible",
parent = Some(foldable[A, B]),
"reduceLeftTo consistent with reduceMap" -> forAll(laws.reduceLeftToConsistentWithReduceMap[A, B] _),
"reduceRightTo consistent with reduceMap" -> forAll(laws.reduceRightToConsistentWithReduceMap[A, B] _)
"reduceRightTo consistent with reduceMap" -> forAll(laws.reduceRightToConsistentWithReduceMap[A, B] _),
"traverse1_ consistent with traverse_" -> forAll(laws.traverseConsistent[G, A, B] _),
"sequence1_ consistent with sequence_" -> forAll(laws.sequenceConsistent[G, A] _)
)
}

Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.3")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.4")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.7")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.8")
addSbtPlugin("com.github.tkawachi" % "sbt-doctest" % "0.3.5")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1")
41 changes: 41 additions & 0 deletions tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cats
package tests

import cats.data.{Xor, XorT}

class ApplicativeErrorCheck extends CatsSuite {

type ErrorOr[A] = String Xor A

val failed: String Xor Int =
"Badness".raiseError[ErrorOr, Int]

test("raiseError syntax creates an Xor with the correct type parameters") {
failed should === ("Badness".left[Int])
}

test("handleError syntax transforms an error to a success") {
failed.handleError(error => error.length) should === (7.right)
}

test("handleErrorWith transforms an error to a success") {
failed.handleErrorWith(error => error.length.right) should === (7.right)
}

test("attempt syntax creates a wrapped Xor") {
failed.attempt should === ("Badness".left.right)
}

test("attemptT syntax creates an XorT") {
failed.attemptT should === (XorT[ErrorOr, String, Int](failed.right))
}

test("recover syntax transforms an error to a success") {
failed.recover { case error => error.length } should === (7.right)
}

test("recoverWith transforms an error to a success") {
failed.recoverWith { case error => error.length.right } should === (7.right)
}

}
15 changes: 15 additions & 0 deletions tests/src/test/scala/cats/tests/BifoldableTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cats
package tests

import cats.data.Xor
import cats.laws.discipline.{BifoldableTests, SerializableTests}
import cats.laws.discipline.arbitrary._

class BifoldableTest extends CatsSuite {
type EitherXor[A, B] = Either[Xor[A, B], Xor[A, B]]
val eitherComposeXor: Bifoldable[EitherXor] =
Bifoldable[Either].compose[Xor]

checkAll("Either compose Xor", BifoldableTests(eitherComposeXor).bifoldable[Int, Int, Int])
checkAll("Bifoldable[Either compose Xor]", SerializableTests.serializable(eitherComposeXor))
}
Loading

0 comments on commit 2dc050d

Please sign in to comment.