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

Add FunctionK and #1480

Merged
merged 1 commit into from
Jan 3, 2017
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
18 changes: 17 additions & 1 deletion core/src/main/scala/cats/arrow/FunctionK.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package arrow

import cats.data.Coproduct
import cats.data.{Coproduct, Prod}

import cats.macros.MacroCompat

Expand Down Expand Up @@ -45,6 +45,22 @@ trait FunctionK[F[_], G[_]] extends Serializable { self =>
*/
def or[H[_]](h: FunctionK[H, G]): FunctionK[Coproduct[F, H, ?], G] =
λ[FunctionK[Coproduct[F, H, ?], G]](fa => fa.fold(self, h))

/**
* Composes two instances of `FunctionK` into a new `FunctionK` that transforms
* one single functor to a [[cats.data.Prod]] of two functors.
*
* {{{
* scala> import cats.arrow.FunctionK
* scala> val list2option = λ[FunctionK[List, Option]](_.headOption)
* scala> val list2vector = λ[FunctionK[List, Vector]](_.toVector)
* scala> val optionAndVector = list2option and list2vector
* scala> optionAndVector(List(1,2,3))
* res0: cats.data.Prod[Option,Vector,Int] = Prod(Some(1),Vector(1, 2, 3))
* }}}
*/
def and[H[_]](h: FunctionK[F, H]): FunctionK[F, Prod[G, H, ?]] =
λ[FunctionK[F, Prod[G, H, ?]]](fa => Prod(self(fa), h(fa)))
}

object FunctionK {
Expand Down
14 changes: 2 additions & 12 deletions docs/src/main/tut/datatypes/freeapplicative.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,24 +153,14 @@ Another useful property `Applicative`s have over `Monad`s is that given two `App
case for monads.

Therefore, we can write an interpreter that uses the product of the `ParValidator` and `Log` `Applicative`s
to interpret our program in one go.
to interpret our program in one go. We can create this interpreter easily by using `FunctionK#and`.

```tut:silent
import cats.data.Prod

type ValidateAndLog[A] = Prod[ParValidator, Log, A]

val prodCompiler =
λ[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 prodCompiler: FunctionK[ValidationOp, ValidateAndLog] = parCompiler and logCompiler

val prodValidation = prog.foldMap[ValidateAndLog](prodCompiler)
```
Expand Down
21 changes: 13 additions & 8 deletions tests/src/test/scala/cats/tests/FunctionKTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ import cats.laws.discipline.arbitrary._
class FunctionKTests extends CatsSuite {

val listToOption = λ[FunctionK[List, Option]](_.headOption)

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


sealed trait Test1Algebra[A] {
def v : A
}
Expand All @@ -25,11 +24,8 @@ class FunctionKTests extends CatsSuite {

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

val Test1NT = λ[FunctionK[Test1Algebra,Id]](_.v)

val Test2NT = λ[FunctionK[Test2Algebra,Id]](_.v)

type T[A] = Coproduct[Test1Algebra, Test2Algebra, A]
val Test1FK = λ[FunctionK[Test1Algebra,Id]](_.v)
val Test2FK = λ[FunctionK[Test2Algebra,Id]](_.v)

test("compose") {
forAll { (list: List[Int]) =>
Expand All @@ -52,13 +48,22 @@ class FunctionKTests extends CatsSuite {
}

test("or") {
val combinedInterpreter = Test1NT or Test2NT
val combinedInterpreter = Test1FK or Test2FK
forAll { (a : Int, b : Int) =>
combinedInterpreter(Coproduct.left(Test1(a))) should === (a)
combinedInterpreter(Coproduct.right(Test2(b))) should === (b)
}
}

test("and") {
val combinedInterpreter = listToOption and listToVector
forAll { (list : List[Int]) =>
val prod = combinedInterpreter(list)
prod.first should === (list.headOption)
prod.second should === (list.toVector)
}
}

test("lift simple unary") {
def optionToList[A](option: Option[A]): List[A] = option.toList
val fOptionToList = FunctionK.lift(optionToList _)
Expand Down