Skip to content

Commit

Permalink
Rename Inject to InjectK (#1596)
Browse files Browse the repository at this point in the history
  • Loading branch information
andyscott authored and kailuowang committed Apr 21, 2017
1 parent 01711b0 commit 50741da
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import cats.arrow.FunctionK
import cats.data.EitherK

/**
* Inject type class as described in "Data types a la carte" (Swierstra 2008).
* The injection type class as described in "Data types a la carte"
* (Swierstra 2008).
*
* @see [[http://www.staff.science.uu.nl/~swier004/publications/2008-jfp.pdf]]
*/
sealed abstract class Inject[F[_], G[_]] {
sealed abstract class InjectK[F[_], G[_]] {
def inj: FunctionK[F, G]

def prj: FunctionK[G, λ[α => Option[F[α]]]]
Expand All @@ -18,29 +19,29 @@ sealed abstract class Inject[F[_], G[_]] {
def unapply[A](ga: G[A]): Option[F[A]] = prj(ga)
}

private[cats] sealed abstract class InjectInstances {
implicit def catsReflexiveInjectInstance[F[_]]: Inject[F, F] =
new Inject[F, F] {
private[cats] sealed abstract class InjectKInstances {
implicit def catsReflexiveInjectKInstance[F[_]]: InjectK[F, F] =
new InjectK[F, F] {
val inj = λ[FunctionK[F, F]](identity(_))

val prj = λ[FunctionK[F, λ[α => Option[F[α]]]]](Some(_))
}

implicit def catsLeftInjectInstance[F[_], G[_]]: Inject[F, EitherK[F, G, ?]] =
new Inject[F, EitherK[F, G, ?]] {
implicit def catsLeftInjectKInstance[F[_], G[_]]: InjectK[F, EitherK[F, G, ?]] =
new InjectK[F, EitherK[F, G, ?]] {
val inj = λ[FunctionK[F, EitherK[F, G, ?]]](EitherK.leftc(_))

val prj = λ[FunctionK[EitherK[F, G, ?], λ[α => Option[F[α]]]]](_.run.left.toOption)
}

implicit def catsRightInjectInstance[F[_], G[_], H[_]](implicit I: Inject[F, G]): Inject[F, EitherK[H, G, ?]] =
new Inject[F, EitherK[H, G, ?]] {
implicit def catsRightInjectKInstance[F[_], G[_], H[_]](implicit I: InjectK[F, G]): InjectK[F, EitherK[H, G, ?]] =
new InjectK[F, EitherK[H, G, ?]] {
val inj = λ[FunctionK[G, EitherK[H, G, ?]]](EitherK.rightc(_)) compose I.inj

val prj = λ[FunctionK[EitherK[H, G, ?], λ[α => Option[F[α]]]]](_.run.right.toOption.flatMap(I.prj(_)))
}
}

object Inject extends InjectInstances {
def apply[F[_], G[_]](implicit I: Inject[F, G]): Inject[F, G] = I
object InjectK extends InjectKInstances {
def apply[F[_], G[_]](implicit I: InjectK[F, G]): InjectK[F, G] = I
}
8 changes: 4 additions & 4 deletions core/src/main/scala/cats/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ package object cats {
type = Nothing
type = Any

/** [[cats.Inject]][F, G] */
type :<:[F[_], G[_]] = Inject[F, G]
/** [[cats.InjectK]][F, G] */
type :<:[F[_], G[_]] = InjectK[F, G]

/** [[cats.Inject]][F, G] */
type :≺:[F[_], G[_]] = Inject[F, G]
/** [[cats.InjectK]][F, G] */
type :≺:[F[_], G[_]] = InjectK[F, G]

/**
* Identity, encoded as `type Id[A] = A`, a convenient alias to make
Expand Down
24 changes: 12 additions & 12 deletions docs/src/main/tut/datatypes/freemonad.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,15 +298,15 @@ val result: (Map[String, Any], Option[Int]) = program.foldMap(pureCompiler).run(
## Composing Free monads ADTs.

Real world applications often time combine different algebras.
The `Inject` type class described by Swierstra in [Data types à la carte](http://www.staff.science.uu.nl/~swier004/publications/2008-jfp.pdf)
The injection type class described by Swierstra in [Data types à la carte](http://www.staff.science.uu.nl/~swier004/publications/2008-jfp.pdf)
lets us compose different algebras in the context of `Free`.

Let's see a trivial example of unrelated ADT's getting composed as a `EitherK` that can form a more complex program.

```tut:silent
import cats.data.EitherK
import cats.free.Free
import cats.{Id, Inject, ~>}
import cats.{Id, InjectK, ~>}
import scala.collection.mutable.ListBuffer
```

Expand All @@ -331,22 +331,22 @@ type CatsApp[A] = EitherK[DataOp, Interact, A]
In order to take advantage of monadic composition we use smart constructors to lift our Algebra to the `Free` context.

```tut:silent
class Interacts[F[_]](implicit I: Inject[Interact, F]) {
class Interacts[F[_]](implicit I: InjectK[Interact, F]) {
def tell(msg: String): Free[F, Unit] = Free.inject[Interact, F](Tell(msg))
def ask(prompt: String): Free[F, String] = Free.inject[Interact, F](Ask(prompt))
}
object Interacts {
implicit def interacts[F[_]](implicit I: Inject[Interact, F]): Interacts[F] = new Interacts[F]
implicit def interacts[F[_]](implicit I: InjectK[Interact, F]): Interacts[F] = new Interacts[F]
}
class DataSource[F[_]](implicit I: Inject[DataOp, F]) {
class DataSource[F[_]](implicit I: InjectK[DataOp, F]) {
def addCat(a: String): Free[F, Unit] = Free.inject[DataOp, F](AddCat(a))
def getAllCats: Free[F, List[String]] = Free.inject[DataOp, F](GetAllCats())
}
object DataSource {
implicit def dataSource[F[_]](implicit I: Inject[DataOp, F]): DataSource[F] = new DataSource[F]
implicit def dataSource[F[_]](implicit I: InjectK[DataOp, F]): DataSource[F] = new DataSource[F]
}
```

Expand Down Expand Up @@ -495,9 +495,9 @@ right-associated structure not subject to quadratic complexity.

## FreeT

Often times we want to interleave the syntax tree when building a Free monad
with some other effect not declared as part of the ADT.
FreeT solves this problem by allowing us to mix building steps of the AST
Often times we want to interleave the syntax tree when building a Free monad
with some other effect not declared as part of the ADT.
FreeT solves this problem by allowing us to mix building steps of the AST
with calling action in other base monad.

In the following example a basic console application is shown.
Expand All @@ -521,7 +521,7 @@ type TeletypeT[M[_], A] = FreeT[Teletype, M, A]
type Log = List[String]
/** Smart constructors, notice we are abstracting over any MonadState instance
* to potentially support other types beside State
* to potentially support other types beside State
*/
class TeletypeOps[M[_]](implicit MS : MonadState[M, Log]) {
def writeLine(line : String) : TeletypeT[M, Unit] =
Expand All @@ -543,11 +543,11 @@ def program(implicit TO : TeletypeOps[TeletypeState]) : TeletypeT[TeletypeState,
userSaid <- TO.readLine("what's up?!")
_ <- TO.log(s"user said : $userSaid")
_ <- TO.writeLine("thanks, see you soon!")
} yield ()
} yield ()
}
def interpreter = new (Teletype ~> TeletypeState) {
def apply[A](fa : Teletype[A]) : TeletypeState[A] = {
def apply[A](fa : Teletype[A]) : TeletypeState[A] = {
fa match {
case ReadLine(prompt) =>
println(prompt)
Expand Down
6 changes: 3 additions & 3 deletions docs/src/main/tut/typeclasses/symbols.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Symbols

Below is a list of symbols used in cats.
Below is a list of symbols used in cats.

The `~>`, `` and `` symbols can be imported with `import cats._`.

Expand All @@ -25,7 +25,7 @@ A scaladoc generated list is also available on the [Scaladoc symbols page](http:
| `x |+| y` | Semigroup combine | [`Semigroup[A]`]({{ site.sources }}/kernel/src/main/scala/cats/kernel/Semigroup.scala) | `combine(x: A, y: A): A` |
| `x <+> y` | SemigroupK combine | [`SemigroupK[F[_]]`]({{ site.sources }}/core/src/main/scala/cats/SemigroupK.scala) | `combineK(x: F[A], y: F[A]): F[A]` |
| `F ~> G` | natural transformation | [`FunctionK[F[_], G[_]]`]({{ site.sources }}/core/src/main/scala/cats/arrow/FunctionK.scala)| `FunctionK` alias |
| `F :<: G` | inject | [`Inject[F[_], G[_]]`]({{ site.sources }}/free/src/main/scala/cats/free/package.scala) | `Inject` alias |
| `F :≺: G` | inject | [`Inject[F[_], G[_]]`]({{ site.sources }}/free/src/main/scala/cats/free/package.scala) | `Inject` alias |
| `F :<: G` | inject | [`InjectK[F[_], G[_]]`]({{ site.sources }}/free/src/main/scala/cats/free/package.scala) | `InjectK` alias |
| `F :≺: G` | inject | [`InjectK[F[_], G[_]]`]({{ site.sources }}/free/src/main/scala/cats/free/package.scala) | `InjectK` alias |
| `` | bottom | [N/A]({{ site.sources }}/core/src/main/scala/cats/package.scala) | `Nothing` |
| `` | top | [N/A]({{ site.sources }}/core/src/main/scala/cats/package.scala) | `Any` |
20 changes: 10 additions & 10 deletions free/src/main/scala/cats/free/Free.scala
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ sealed abstract class Free[S[_], A] extends Product with Serializable {
}(Free.catsFreeMonadForFree)

/**
* Lift into `G` (typically a `EitherK`) given `Inject`. Analogous
* Lift into `G` (typically a `EitherK`) given `InjectK`. Analogous
* to `Free.inject` but lifts programs rather than constructors.
*
*{{{
Expand All @@ -154,7 +154,7 @@ sealed abstract class Free[S[_], A] extends Product with Serializable {
*res4: cats.free.Free[Lo,String] = Free(...)
*}}}
*/
final def inject[G[_]](implicit ev: Inject[S, G]): Free[G, A] =
final def inject[G[_]](implicit ev: InjectK[S, G]): Free[G, A] =
compile(λ[S ~> G](ev.inj(_)))

override def toString: String =
Expand Down Expand Up @@ -209,28 +209,28 @@ object Free {
λ[FunctionK[Free[F, ?], M]](f => f.foldMap(fk))

/**
* This method is used to defer the application of an Inject[F, G]
* This method is used to defer the application of an InjectK[F, G]
* instance. The actual work happens in
* `FreeInjectPartiallyApplied#apply`.
* `FreeInjectKPartiallyApplied#apply`.
*
* This method exists to allow the `F` and `G` parameters to be
* bound independently of the `A` parameter below.
*/
def inject[F[_], G[_]]: FreeInjectPartiallyApplied[F, G] =
new FreeInjectPartiallyApplied
def inject[F[_], G[_]]: FreeInjectKPartiallyApplied[F, G] =
new FreeInjectKPartiallyApplied

/**
* Pre-application of an injection to a `F[A]` value.
*/
final class FreeInjectPartiallyApplied[F[_], G[_]] private[free] {
def apply[A](fa: F[A])(implicit I: Inject[F, G]): Free[G, A] =
final class FreeInjectKPartiallyApplied[F[_], G[_]] private[free] {
def apply[A](fa: F[A])(implicit I: InjectK[F, G]): Free[G, A] =
Free.liftF(I.inj(fa))
}

def injectRoll[F[_], G[_], A](ga: G[Free[F, A]])(implicit I: Inject[G, F]): Free[F, A] =
def injectRoll[F[_], G[_], A](ga: G[Free[F, A]])(implicit I: InjectK[G, F]): Free[F, A] =
Free.roll(I.inj(ga))

def match_[F[_], G[_], A](fa: Free[F, A])(implicit F: Functor[F], I: Inject[G, F]): Option[G[Free[F, A]]] =
def match_[F[_], G[_], A](fa: Free[F, A])(implicit F: Functor[F], I: InjectK[G, F]): Option[G[Free[F, A]]] =
fa.resume.fold(I.prj(_), _ => None)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import cats.data.EitherK
import cats.tests.CatsSuite
import org.scalacheck._

class InjectTests extends CatsSuite {
class InjectKTests extends CatsSuite {

sealed trait Test1Algebra[A]

Expand Down Expand Up @@ -52,8 +52,8 @@ class InjectTests extends CatsSuite {
} yield x + y

forAll { (x: Int, y: Int) =>
val expr1: T[Int] = Inject[Test1Algebra, T].inj(Test1(x, _ + 1))
val expr2: T[Int] = Inject[Test2Algebra, T].inj(Test2(y, _ * 2))
val expr1: T[Int] = InjectK[Test1Algebra, T].inj(Test1(x, _ + 1))
val expr2: T[Int] = InjectK[Test2Algebra, T].inj(Test2(y, _ * 2))
val res = distr[T, Int](expr1, expr2)
res should ===(Some(x + y))
}
Expand All @@ -71,29 +71,29 @@ class InjectTests extends CatsSuite {
} yield x + y

forAll { (x: Int, y: Int) =>
val expr1: T[Int] = Inject[Test1Algebra, T].apply(Test1(x, _ + 1))
val expr2: T[Int] = Inject[Test2Algebra, T].apply(Test2(y, _ * 2))
val expr1: T[Int] = InjectK[Test1Algebra, T].apply(Test1(x, _ + 1))
val expr2: T[Int] = InjectK[Test2Algebra, T].apply(Test2(y, _ * 2))
val res = distr[T, Int](expr1, expr2)
res should ===(Some(x + y))
}
}

test("apply in left") {
forAll { (y: Test1Algebra[Int]) =>
Inject[Test1Algebra, T].inj(y) == EitherK(Left(y)) should ===(true)
InjectK[Test1Algebra, T].inj(y) == EitherK(Left(y)) should ===(true)
}
}

test("apply in right") {
forAll { (y: Test2Algebra[Int]) =>
Inject[Test2Algebra, T].inj(y) == EitherK(Right(y)) should ===(true)
InjectK[Test2Algebra, T].inj(y) == EitherK(Right(y)) should ===(true)
}
}

test("null identity") {
val listIntNull = null.asInstanceOf[List[Int]]
Inject.catsReflexiveInjectInstance[List].inj[Int](listIntNull) should ===(listIntNull)
Inject.catsReflexiveInjectInstance[List].prj[Int](listIntNull) should ===(Some(listIntNull))
InjectK.catsReflexiveInjectKInstance[List].inj[Int](listIntNull) should ===(listIntNull)
InjectK.catsReflexiveInjectKInstance[List].prj[Int](listIntNull) should ===(Some(listIntNull))
}

}

0 comments on commit 50741da

Please sign in to comment.