Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updated tests and docs to use polymorphic lambda to create FunctionK #1386

Merged
merged 1 commit into from
Sep 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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