Skip to content

Commit

Permalink
Merge pull request #1386 from kailuowang/update-kind-projector
Browse files Browse the repository at this point in the history
updated tests and docs to use polymorphic lambda to create `FunctionK`
  • Loading branch information
kailuowang authored Sep 23, 2016
2 parents 7b15008 + 23a0f59 commit b8603ec
Show file tree
Hide file tree
Showing 10 changed files with 44 additions and 75 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ lazy val commonSettings = Seq(
"com.github.mpilquist" %%% "simulacrum" % "0.8.0",
"org.typelevel" %%% "machinist" % "0.4.1",
compilerPlugin("org.scalamacros" %% "paradise" % "2.1.0" cross CrossVersion.full),
compilerPlugin("org.spire-math" %% "kind-projector" % "0.6.3")
compilerPlugin("org.spire-math" %% "kind-projector" % "0.9.0")
),
fork in test := true,
parallelExecution in Test := false,
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala/cats/arrow/FunctionK.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import cats.macros.MacroCompat
* `FunctionK[F[_], G[_]]` is a functor transformation from `F` to `G`
* in the same manner that function `A => B` is a morphism from values
* of type `A` to `B`.
* An easy way to create a FunctionK instance is to use the Polymorphic
* lambdas provided by non/kind-projector v0.9+. E.g.
* {{{
* val listToOption = λ[FunctionK[List, Option]](_.headOption)
* }}}
*/
trait FunctionK[F[_], G[_]] extends Serializable { self =>

Expand Down
5 changes: 1 addition & 4 deletions core/src/main/scala/cats/data/Coproduct.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ final case class Coproduct[F[_], G[_], A](run: Either[F[A], G[A]]) {
* {{{
* scala> import cats.arrow.FunctionK
* scala> import cats.data.Coproduct
* scala> val listToOption =
* | new FunctionK[List, Option] {
* | def apply[A](fa: List[A]): Option[A] = fa.headOption
* | }
* scala> val listToOption = λ[FunctionK[List, Option]](_.headOption)
* scala> val optionToOption = FunctionK.id[Option]
* scala> val cp1: Coproduct[List, Option, Int] = Coproduct.leftc(List(1,2,3))
* scala> val cp2: Coproduct[List, Option, Int] = Coproduct.rightc(Some(4))
Expand Down
49 changes: 20 additions & 29 deletions docs/src/main/tut/freeapplicative.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,13 @@ import cats.implicits._
type FromString[A] = String => A
val compiler =
new FunctionK[ValidationOp, FromString] {
def apply[A](fa: ValidationOp[A]): String => A =
λ[FunctionK[ValidationOp, FromString]] { fa =>
str =>
fa match {
case Size(size) => str.size >= size
case HasNumber => str.exists(c => "0123456789".contains(c))
}
}
}
```

```tut:book
Expand Down Expand Up @@ -103,14 +102,13 @@ import scala.concurrent.ExecutionContext.Implicits.global
type ParValidator[A] = Kleisli[Future, String, A]
val parCompiler =
new FunctionK[ValidationOp, ParValidator] {
def apply[A](fa: ValidationOp[A]): ParValidator[A] =
Kleisli { str =>
fa match {
case Size(size) => Future { str.size >= size }
case HasNumber => Future { str.exists(c => "0123456789".contains(c)) }
}
λ[FunctionK[ValidationOp, ParValidator]] { fa =>
Kleisli { str =>
fa match {
case Size(size) => Future { str.size >= size }
case HasNumber => Future { str.exists(c => "0123456789".contains(c)) }
}
}
}
val parValidation = prog.foldMap[ParValidator](parCompiler)
Expand All @@ -130,12 +128,9 @@ import cats.implicits._
type Log[A] = Const[List[String], A]
val logCompiler =
new FunctionK[ValidationOp, Log] {
def apply[A](fa: ValidationOp[A]): Log[A] =
fa match {
case Size(size) => Const(List(s"size >= $size"))
case HasNumber => Const(List("has number"))
}
λ[FunctionK[ValidationOp, Log]] {
case Size(size) => Const(List(s"size >= $size"))
case HasNumber => Const(List("has number"))
}
def logValidation[A](validation: Validation[A]): List[String] =
Expand Down Expand Up @@ -166,19 +161,15 @@ import cats.data.Prod
type ValidateAndLog[A] = Prod[ParValidator, Log, A]
val prodCompiler =
new FunctionK[ValidationOp, ValidateAndLog] {
def apply[A](fa: ValidationOp[A]): ValidateAndLog[A] = {
fa match {
case Size(size) =>
val f: ParValidator[Boolean] = Kleisli(str => Future { str.size >= size })
val l: Log[Boolean] = Const(List(s"size > $size"))
Prod[ParValidator, Log, Boolean](f, l)
case HasNumber =>
val f: ParValidator[Boolean] = Kleisli(str => Future(str.exists(c => "0123456789".contains(c))))
val l: Log[Boolean] = Const(List("has number"))
Prod[ParValidator, Log, Boolean](f, l)
}
}
λ[FunctionK[ValidationOp, ValidateAndLog]] {
case Size(size) =>
val f: ParValidator[Boolean] = Kleisli(str => Future { str.size >= size })
val l: Log[Boolean] = Const(List(s"size > $size"))
Prod[ParValidator, Log, Boolean](f, l)
case HasNumber =>
val f: ParValidator[Boolean] = Kleisli(str => Future(str.exists(c => "0123456789".contains(c))))
val l: Log[Boolean] = Const(List("has number"))
Prod[ParValidator, Log, Boolean](f, l)
}
val prodValidation = prog.foldMap[ValidateAndLog](prodCompiler)
Expand Down
5 changes: 1 addition & 4 deletions free/src/test/scala/cats/free/CoyonedaTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ class CoyonedaTests extends CatsSuite {
}

test("transform and run is same as applying natural trans") {
val nt =
new FunctionK[Option, List] {
def apply[A](fa: Option[A]): List[A] = fa.toList
}
val nt = λ[FunctionK[Option, List]](_.toList)
val o = Option("hello")
val c = Coyoneda.lift(o)
c.transform(nt).run should === (nt(o))
Expand Down
17 changes: 5 additions & 12 deletions free/src/test/scala/cats/free/FreeApplicativeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ class FreeApplicativeTests extends CatsSuite {
val f = x.map(i => (j: Int) => i + j)
val r1 = y.ap(f)
val r2 = r1.monad
val nt =
new FunctionK[Id, Id] {
def apply[A](fa: Id[A]): Id[A] = fa
}
val nt = FunctionK.id[Id]
r1.foldMap(nt) should === (r2.foldMap(nt))
}

Expand All @@ -81,9 +78,7 @@ class FreeApplicativeTests extends CatsSuite {

test("FreeApplicative#analyze") {
type G[A] = List[Int]
val countingNT = new FunctionK[List, G] {
def apply[A](la: List[A]): G[A] = List(la.length)
}
val countingNT = λ[FunctionK[List, G]](la => List(la.length))

val fli1 = FreeApplicative.lift[List, Int](List(1, 3, 5, 7))
fli1.analyze[G[Int]](countingNT) should === (List(4))
Expand All @@ -103,8 +98,8 @@ class FreeApplicativeTests extends CatsSuite {

type Tracked[A] = State[String, A]

val f: FunctionK[Foo,Tracked] = new FunctionK[Foo,Tracked] {
def apply[A](fa: Foo[A]): Tracked[A] = State[String, A]{ s0 =>
val f = λ[FunctionK[Foo,Tracked]] { fa =>
State { s0 =>
(s0 + fa.toString + ";", fa.getA)
}
}
Expand All @@ -126,9 +121,7 @@ class FreeApplicativeTests extends CatsSuite {

val z = Apply[Dsl].map2(x, y)((_, _) => ())

val asString: FunctionK[Id, λ[α => String]] = new FunctionK[Id, λ[α => String]] {
def apply[A](a: A): String = a.toString
}
val asString = λ[FunctionK[Id, λ[α => String]]](_.toString)

z.analyze(asString) should === ("xy")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ class FreeInvariantMonoidalTests extends CatsSuite {

test("FreeInvariantMonoidal#analyze") {
type G[A] = List[Int]
val countingNT = new FunctionK[List, G] {
def apply[A](la: List[A]): G[A] = List(la.length)
}
val countingNT = λ[FunctionK[List, G]](la => List(la.length))

val fli1 = FreeInvariantMonoidal.lift[List, Int](List(1, 3, 5, 7))
fli1.analyze[G[Int]](countingNT) should === (List(4))
Expand Down
10 changes: 3 additions & 7 deletions free/src/test/scala/cats/free/FreeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,8 @@ class FreeTests extends CatsSuite {
z <- if (j<10000) a(j) else Free.pure[FTestApi, Int](j)
} yield z

def runner: FunctionK[FTestApi,Id] = new FunctionK[FTestApi,Id] {
def apply[A](fa: FTestApi[A]): Id[A] = fa match {
case TB(i) => i+1
}
def runner: FunctionK[FTestApi,Id] = λ[FunctionK[FTestApi,Id]] {
case TB(i) => i+1
}

assert(10000 == a(0).foldMap(runner))
Expand Down Expand Up @@ -117,9 +115,7 @@ object FreeTests extends FreeTestsInstances {
}

sealed trait FreeTestsInstances {
val headOptionU: FunctionK[List,Option] = new FunctionK[List,Option] {
def apply[A](fa: List[A]): Option[A] = fa.headOption
}
val headOptionU = λ[FunctionK[List,Option]](_.headOption)

private def freeGen[F[_], A](maxDepth: Int)(implicit F: Arbitrary[F[A]], A: Arbitrary[A]): Gen[Free[F, A]] = {
val noFlatMapped = Gen.oneOf(
Expand Down
20 changes: 6 additions & 14 deletions tests/src/test/scala/cats/tests/FunctionKTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,11 @@ import cats.data.NonEmptyList
import cats.laws.discipline.arbitrary._

class FunctionKTests extends CatsSuite {
val listToOption =
new FunctionK[List, Option] {
def apply[A](fa: List[A]): Option[A] = fa.headOption
}

val optionToList =
new FunctionK[Option, List] {
def apply[A](fa: Option[A]): List[A] = fa.toList
}
val listToOption = λ[FunctionK[List, Option]](_.headOption)

val optionToList = λ[FunctionK[Option, List]](_.toList)


sealed trait Test1Algebra[A] {
def v : A
Expand All @@ -29,13 +25,9 @@ class FunctionKTests extends CatsSuite {

case class Test2[A](v : A) extends Test2Algebra[A]

object Test1NT extends FunctionK[Test1Algebra,Id] {
override def apply[A](fa: Test1Algebra[A]): Id[A] = fa.v
}
val Test1NT = λ[FunctionK[Test1Algebra,Id]](_.v)

object Test2NT extends FunctionK[Test2Algebra,Id] {
override def apply[A](fa: Test2Algebra[A]): Id[A] = fa.v
}
val Test2NT = λ[FunctionK[Test2Algebra,Id]](_.v)

type T[A] = Coproduct[Test1Algebra, Test2Algebra, A]

Expand Down
2 changes: 1 addition & 1 deletion tests/src/test/scala/cats/tests/KleisliTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ class KleisliTests extends CatsSuite {

test("transform") {
val opt = Kleisli { (x: Int) => Option(x.toDouble) }
val optToList = new FunctionK[Option,List] { def apply[A](fa: Option[A]): List[A] = fa.toList }
val optToList = λ[FunctionK[Option,List]](_.toList)
val list = opt.transform(optToList)

val is = 0.to(10).toList
Expand Down

0 comments on commit b8603ec

Please sign in to comment.