Skip to content

Commit

Permalink
Merge branch 'master' into kailuowang-patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
kailuowang authored Oct 31, 2017
2 parents f17178e + 94f4928 commit 285e800
Show file tree
Hide file tree
Showing 71 changed files with 850 additions and 296 deletions.
2 changes: 1 addition & 1 deletion alleycats-core/src/main/scala/alleycats/std/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ import export._
SetInstances,
TryInstances,
IterableInstances
) object all extends LegacySetInstances with LegacyTryInstances with LegacyIterableInstances
) object all extends LegacySetInstances with LegacyTryInstances with LegacyIterableInstances with MapInstances
1 change: 0 additions & 1 deletion alleycats-core/src/main/scala/alleycats/std/iterable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,4 @@ object IterableInstances {
// TODO: remove when cats.Foldable support export-hook
trait LegacyIterableInstances {
implicit def legacyIterableFoldable(implicit e: ExportOrphan[Foldable[Iterable]]): Foldable[Iterable] = e.instance

}
46 changes: 46 additions & 0 deletions alleycats-core/src/main/scala/alleycats/std/map.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package alleycats
package std

import cats._

trait MapInstances {

// toList is inconsistent. See https://github.com/typelevel/cats/issues/1831
implicit def alleycatsStdInstancesForMap[K]: Traverse[Map[K, ?]] =
new Traverse[Map[K, ?]] {

def traverse[G[_], A, B](fa: Map[K, A])(f: A => G[B])(implicit G: Applicative[G]): G[Map[K, B]] = {
val gba: Eval[G[Map[K, B]]] = Always(G.pure(Map.empty))
val gbb = Foldable.iterateRight(fa, gba){ (kv, lbuf) =>
G.map2Eval(f(kv._2), lbuf)({ (b, buf) => buf + (kv._1 -> b)})
}.value
G.map(gbb)(_.toMap)
}

override def map[A, B](fa: Map[K, A])(f: A => B): Map[K, B] =
fa.map { case (k, a) => (k, f(a)) }

def foldLeft[A, B](fa: Map[K, A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b) { case (x, (k, a)) => f(x, a)}

def foldRight[A, B](fa: Map[K, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Foldable.iterateRight(fa.values, lb)(f)

override def size[A](fa: Map[K, A]): Long = fa.size.toLong

override def get[A](fa: Map[K, A])(idx: Long): Option[A] =
if (idx < 0L || Int.MaxValue < idx) None
else {
val n = idx.toInt
if (n >= fa.size) None
else Some(fa.valuesIterator.drop(n).next)
}

override def isEmpty[A](fa: Map[K, A]): Boolean = fa.isEmpty

override def fold[A](fa: Map[K, A])(implicit A: Monoid[A]): A =
A.combineAll(fa.values)

override def toList[A](fa: Map[K, A]): List[A] = fa.values.toList
}
}
35 changes: 34 additions & 1 deletion alleycats-core/src/main/scala/alleycats/std/set.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package alleycats.std

import cats.{Applicative, Eval, Foldable, Monad, Traverse}
import cats.{Applicative, Eval, Foldable, Monad, Monoid, Traverse}
import export._

import scala.annotation.tailrec
Expand Down Expand Up @@ -67,12 +67,45 @@ object SetInstances {
fa.foldLeft(b)(f)
def foldRight[A, B](fa: Set[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Foldable.iterateRight(fa, lb)(f)

def traverse[G[_]: Applicative, A, B](sa: Set[A])(f: A => G[B]): G[Set[B]] = {
val G = Applicative[G]
sa.foldLeft(G.pure(Set.empty[B])) { (buf, a) =>
G.map2(buf, f(a))(_ + _)
}
}

override def get[A](fa: Set[A])(idx: Long): Option[A] = {
@tailrec
def go(idx: Int, it: Iterator[A]): Option[A] = {
if (it.hasNext) {
if (idx == 0) Some(it.next) else {
it.next
go(idx - 1, it)
}
} else None
}
if (idx < Int.MaxValue && idx >= 0L) go(idx.toInt, fa.toIterator) else None
}

override def size[A](fa: Set[A]): Long = fa.size.toLong

override def exists[A](fa: Set[A])(p: A => Boolean): Boolean =
fa.exists(p)

override def forall[A](fa: Set[A])(p: A => Boolean): Boolean =
fa.forall(p)

override def isEmpty[A](fa: Set[A]): Boolean = fa.isEmpty

override def fold[A](fa: Set[A])(implicit A: Monoid[A]): A = A.combineAll(fa)

override def toList[A](fa: Set[A]): List[A] = fa.toList

override def reduceLeftOption[A](fa: Set[A])(f: (A, A) => A): Option[A] =
fa.reduceLeftOption(f)

override def find[A](fa: Set[A])(f: A => Boolean): Option[A] = fa.find(f)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ package alleycats
package tests


import alleycats.std.MapInstances
import catalysts.Platform

import cats._
import cats.instances.AllInstances
import cats.syntax.{AllSyntax, EqOps}
import cats.tests.StrictCatsEquality
import org.scalactic.anyvals.{PosZDouble, PosInt, PosZInt}
import org.scalactic.anyvals.{PosInt, PosZDouble, PosZInt}
import org.scalatest.{FunSuite, Matchers}
import org.scalatest.prop.{Configuration, GeneratorDrivenPropertyChecks}
import org.typelevel.discipline.scalatest.Discipline

import org.scalacheck.{Arbitrary, Gen}
import org.scalacheck.Arbitrary.arbitrary

Expand All @@ -37,7 +36,7 @@ trait TestSettings extends Configuration with Matchers {
* An opinionated stack of traits to improve consistency and reduce
* boilerplate in Alleycats tests. Derived from Cats.
*/
trait AlleycatsSuite extends FunSuite with Matchers with GeneratorDrivenPropertyChecks with Discipline with TestSettings with AllInstances with AllSyntax with TestInstances with StrictCatsEquality {
trait AlleycatsSuite extends FunSuite with Matchers with GeneratorDrivenPropertyChecks with Discipline with TestSettings with AllInstances with AllSyntax with TestInstances with StrictCatsEquality with MapInstances {
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
checkConfiguration

Expand Down
16 changes: 6 additions & 10 deletions alleycats-tests/src/test/scala/alleycats/tests/IterableTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@ package alleycats
package tests

import cats.{Eval, Foldable}
import cats.laws.discipline._

import alleycats.std.all._

class IterableTests extends AlleycatsSuite {

checkAll("Foldable[Iterable]", FoldableTests[Iterable].foldable[Int, Int])

test("foldLeft sum == sum"){
val it = Iterable(1, 2, 3)
Foldable[Iterable].foldLeft(it, 0){
case (b, a) => a + b
} shouldEqual(it.sum)
}
test("foldLeft sum == sum"){
val it = Iterable(1, 2, 3)
Foldable[Iterable].foldLeft(it, 0){
case (b, a) => a + b
} shouldEqual(it.sum)
}

test("foldRight early termination"){
Foldable[Iterable].foldRight(Iterable(1, 2, 3), Eval.now("KO")){
Expand Down
8 changes: 8 additions & 0 deletions alleycats-tests/src/test/scala/alleycats/tests/MapSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package alleycats.tests

import cats.laws.discipline.SerializableTests
import cats.Traverse

class MapSuite extends AlleycatsSuite {
checkAll("Traverse[Map[Int, ?]]", SerializableTests.serializable(Traverse[Map[Int, ?]]))
}
16 changes: 16 additions & 0 deletions alleycats-tests/src/test/scala/alleycats/tests/SetSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package alleycats.tests

import alleycats.laws.discipline._
import cats.Foldable
import cats.kernel.laws.discipline.SerializableTests

import alleycats.std.all._

class SetSuite extends AlleycatsSuite {
checkAll("FlatMapRec[Set]", FlatMapRecTests[Set].tailRecM[Int])

checkAll("Foldable[Set]", SerializableTests.serializable(Foldable[Set]))
}



14 changes: 0 additions & 14 deletions alleycats-tests/src/test/scala/alleycats/tests/SetTests.scala

This file was deleted.

1 change: 0 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,6 @@ lazy val macrosJVM = macros.jvm
lazy val macrosJS = macros.js



lazy val kernel = crossProject.crossType(CrossType.Pure)
.in(file("kernel"))
.settings(moduleName := "cats-kernel", name := "Cats kernel")
Expand Down
4 changes: 3 additions & 1 deletion core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import simulacrum.typeclass
/**
* Data structures that can be folded to a summary value.
*
* In the case of a collection (such as `List` or `Set`), these
* In the case of a collection (such as `List` or `Vector`), these
* methods will fold together (combine) the values contained in the
* collection to produce a single result. Most collection types have
* `foldLeft` methods, which will usually be used by the associated
* `Foldable[_]` instance.
*
* Instances of Foldable should be ordered collections to allow for consistent folding.
*
* Foldable[F] is implemented in terms of two basic methods:
*
* - `foldLeft(fa, b)(f)` eagerly folds `fa` from left-to-right.
Expand Down
7 changes: 6 additions & 1 deletion core/src/main/scala/cats/data/EitherK.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ final case class EitherK[F[_], G[_], A](run: Either[F[A], G[A]]) {
def map[B](f: A => B)(implicit F: Functor[F], G: Functor[G]): EitherK[F, G, B] =
EitherK(run.bimap(F.lift(f), G.lift(f)))

/**
* Modify the right side context `G` using transformation `f`.
*/
def mapK[H[_]](f: G ~> H): EitherK[F, H, A] =
EitherK(run.map(f.apply))

def coflatMap[B](f: EitherK[F, G, A] => B)(implicit F: CoflatMap[F], G: CoflatMap[G]): EitherK[F, G, B] =
EitherK(
run.bimap(a => F.coflatMap(a)(x => f(leftc(x))), a => G.coflatMap(a)(x => f(rightc(x))))
Expand Down Expand Up @@ -234,4 +240,3 @@ private[data] trait EitherKComonad[F[_], G[_]] extends Comonad[EitherK[F, G, ?]]
def extract[A](p: EitherK[F, G, A]): A =
p.extract
}

5 changes: 5 additions & 0 deletions core/src/main/scala/cats/data/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) {

def map[D](f: B => D)(implicit F: Functor[F]): EitherT[F, A, D] = bimap(identity, f)

/**
* Modify the context `F` using transformation `f`.
*/
def mapK[G[_]](f: F ~> G): EitherT[G, A, B] = EitherT[G, A, B](f(value))

def semiflatMap[D](f: B => F[D])(implicit F: Monad[F]): EitherT[F, A, D] =
flatMap(b => EitherT.right(f(b)))

Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/Func.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ sealed abstract class Func[F[_], A, B] { self =>
def run: A => F[B]
def map[C](f: B => C)(implicit FF: Functor[F]): Func[F, A, C] =
Func.func(a => FF.map(self.run(a))(f))

/**
* Modify the context `F` using transformation `f`.
*/
def mapK[G[_]](f: F ~> G): Func[G, A, B] =
Func.func(run andThen f.apply)
}

object Func extends FuncInstances {
Expand Down
10 changes: 8 additions & 2 deletions core/src/main/scala/cats/data/IdT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ final case class IdT[F[_], A](value: F[A]) {
def map[B](f: A => B)(implicit F: Functor[F]): IdT[F, B] =
IdT(F.map(value)(f))

/**
* Modify the context `F` using transformation `f`.
*/
def mapK[G[_]](f: F ~> G): IdT[G, A] =
IdT[G, A](f(value))

def flatMap[B](f: A => IdT[F, B])(implicit F: FlatMap[F]): IdT[F, B] =
IdT(F.flatMap(value)(f.andThen(_.value)))

Expand Down Expand Up @@ -151,7 +157,7 @@ private[data] sealed abstract class IdTInstances0 extends IdTInstances1 {
new IdTTraverse[F] { implicit val F0: Traverse[F] = F }

implicit def catsDataEqForIdT[F[_], A](implicit F: Eq[F[A]]): Eq[IdT[F, A]] =
F.on(_.value)
Eq.by[IdT[F, A], F[A]](_.value)
}

private[data] sealed abstract class IdTInstances extends IdTInstances0 {
Expand All @@ -160,7 +166,7 @@ private[data] sealed abstract class IdTInstances extends IdTInstances0 {
new IdTNonEmptyTraverse[F] { implicit val F0: NonEmptyTraverse[F] = F }

implicit def catsDataOrderForIdT[F[_], A](implicit F: Order[F[A]]): Order[IdT[F, A]] =
F.on(_.value)
Order.by[IdT[F, A], F[A]](_.value)

implicit def catsDataShowForIdT[F[_], A](implicit F: Show[F[A]]): Show[IdT[F, A]] =
Contravariant[Show].contramap(F)(_.value)
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/scala/cats/data/IndexedReaderWriterStateT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ final class IndexedReaderWriterStateT[F[_], E, L, SA, SB, A](val runF: F[(E, SA)
def map[B](f: A => B)(implicit F: Functor[F]): IndexedReaderWriterStateT[F, E, L, SA, SB, B] =
transform { (l, s, a) => (l, s, f(a)) }

/**
* Modify the context `F` using transformation `f`.
*/
def mapK[G[_]](f: F ~> G)(implicit F: Functor[F]): IndexedReaderWriterStateT[G, E, L, SA, SB, A] =
IndexedReaderWriterStateT.applyF(
f(F.map(runF)(rwsa => (e, sa) => f(rwsa(e, sa)))))

/**
* Modify the resulting state using `f` and the resulting value using `g`.
*/
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/scala/cats/data/IndexedStateT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ final class IndexedStateT[F[_], SA, SB, A](val runF: F[SA => F[(SB, A)]]) extend
def map[B](f: A => B)(implicit F: Functor[F]): IndexedStateT[F, SA, SB, B] =
transform { case (s, a) => (s, f(a)) }

/**
* Modify the context `F` using transformation `f`.
*/
def mapK[G[_]](f: F ~> G)(implicit F: Functor[F]): IndexedStateT[G, SA, SB, A] =
IndexedStateT.applyF(
f(F.map(runF)(_.andThen(fsa => f(fsa)))))

def contramap[S0](f: S0 => SA)(implicit F: Functor[F]): IndexedStateT[F, S0, SB, A] =
IndexedStateT.applyF {
F.map(runF) { safsba =>
Expand Down
13 changes: 10 additions & 3 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ final case class Kleisli[F[_], A, B](run: A => F[B]) { self =>
def mapF[N[_], C](f: F[B] => N[C]): Kleisli[N, A, C] =
Kleisli(run andThen f)

/**
* Modify the context `F` using transformation `f`.
*/
def mapK[G[_]](f: F ~> G): Kleisli[G, A, B] =
Kleisli[G, A, B](run andThen f.apply)

def flatMap[C](f: B => Kleisli[F, A, C])(implicit F: FlatMap[F]): Kleisli[F, A, C] =
Kleisli((r: A) => F.flatMap[B, C](run(r))((b: B) => f(b).run(r)))

Expand Down Expand Up @@ -48,8 +54,9 @@ final case class Kleisli[F[_], A, B](run: A => F[B]) { self =>
def local[AA](f: AA => A): Kleisli[F, AA, B] =
Kleisli(f.andThen(run))

@deprecated("Use mapK", "1.0.0")
def transform[G[_]](f: FunctionK[F, G]): Kleisli[G, A, B] =
Kleisli(a => f(run(a)))
mapK(f)

def lower(implicit F: Applicative[F]): Kleisli[F, A, F[B]] =
Kleisli(a => F.pure(run(a)))
Expand Down Expand Up @@ -148,10 +155,10 @@ private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2
def monad: Monad[Kleisli[M, A, ?]] = catsDataMonadForKleisli

def sequential: Kleisli[F, A, ?] ~> Kleisli[M, A, ?] =
λ[Kleisli[F, A, ?] ~> Kleisli[M, A, ?]](_.transform(P.sequential))
λ[Kleisli[F, A, ?] ~> Kleisli[M, A, ?]](_.mapK(P.sequential))

def parallel: Kleisli[M, A, ?] ~> Kleisli[F, A, ?] =
λ[Kleisli[M, A, ?] ~> Kleisli[F, A, ?]](_.transform(P.parallel))
λ[Kleisli[M, A, ?] ~> Kleisli[F, A, ?]](_.mapK(P.parallel))
}
}

Expand Down
12 changes: 10 additions & 2 deletions core/src/main/scala/cats/data/Nested.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@ package data
* res1: List[Option[String]] = List(Some(2), None)
* }}}
*/
final case class Nested[F[_], G[_], A](value: F[G[A]])
final case class Nested[F[_], G[_], A](value: F[G[A]]) {

/**
* Modify the context `F` using transformation `f`.
*/
def mapK[H[_]](f: F ~> H): Nested[H, G, A] =
Nested(f(value))

}

object Nested extends NestedInstances

private[data] sealed abstract class NestedInstances extends NestedInstances0 {
implicit def catsDataEqForNested[F[_], G[_], A](implicit FGA: Eq[F[G[A]]]): Eq[Nested[F, G, A]] =
FGA.on(_.value)
Eq.by[Nested[F, G, A], F[G[A]]](_.value)

implicit def catsDataNonEmptyTraverseForNested[F[_]: NonEmptyTraverse, G[_]: NonEmptyTraverse]: NonEmptyTraverse[Nested[F, G, ?]] =
new NestedNonEmptyTraverse[F, G] {
Expand Down
Loading

0 comments on commit 285e800

Please sign in to comment.