Skip to content

Commit

Permalink
Merge pull request #1480 from peterneyens/functionk-and
Browse files Browse the repository at this point in the history
Add FunctionK and
  • Loading branch information
non authored Jan 3, 2017
2 parents 822a2fe + 037b309 commit 87d1ef6
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 21 deletions.
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

0 comments on commit 87d1ef6

Please sign in to comment.