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

Request to Include cats.data.Coproduct and cats.free.Inject #671

Merged
merged 20 commits into from
Nov 19, 2015
Merged

Request to Include cats.data.Coproduct and cats.free.Inject #671

merged 20 commits into from
Nov 19, 2015

Conversation

raulraja
Copy link
Contributor

The following PR adds cats.data.Coproduct and cats.free.Inject largely based on those found in Scalaz as in "Data types a la carte" (Swierstra 2008).

Coproduct & Inject can be used to compose Free based Algebras where an Application is nothing but the Coproduct resulting from the interaction of it's algebras.

In other words:

import cats.arrow.NaturalTransformation
import cats.data.{Xor, Coproduct}
import cats.free.{Inject, Free}
import cats.{Id, ~>}
import scala.collection.mutable.ListBuffer

/* Handles user interaction */
sealed trait Interact[A]
case class Ask(prompt: String) extends Interact[String]
case class Tell(msg: String) extends Interact[Unit]

/* Represents persistence operations */
sealed trait DataOp[A]
case class AddCat(a: String) extends DataOp[Unit]
case class GetAllCats() extends DataOp[List[String]]

type CatsApp[A] = Coproduct[DataOp, Interact, A]

def lift[F[_], G[_], A](fa: F[A])(implicit I: Inject[F, G]): Free[G, A] =
  Free.liftF(I.inj(fa))

class Interacts[F[_]](implicit I: Inject[Interact, F]) {
  def tell(msg: String): Free[F, Unit] = lift(Tell(msg))
  def ask(prompt: String): Free[F, String] = lift(Ask(prompt))
}

object Interacts {
  implicit def interacts[F[_]](implicit I: Inject[Interact, F]): Interacts[F] = new Interacts[F]
}

class DataSource[F[_]](implicit I: Inject[DataOp, F]) {
  def addCat(a: String): Free[F, Unit] = lift[DataOp, F, Unit](AddCat(a))
  def getAllCats: Free[F, List[String]] = lift[DataOp, F, List[String]](GetAllCats())
}

object DataSource {
  implicit def dataSource[F[_]](implicit I: Inject[DataOp, F]): DataSource[F] = new DataSource[F]
}

def program(implicit I : Interacts[CatsApp], D : DataSource[CatsApp]) = {

  import I._, D._

  for {
    cat <- ask("What's the kitty's name")
    _ <- addCat(cat)
    cats <- getAllCats
    _ <- tell(cats.toString)
  } yield ()
}

Interpreters omitted for simplicity but available in the free monads tutorial and Inject tests.

@non @ceedubs thanks for your consideration.

@non
Copy link
Contributor

non commented Nov 17, 2015

This looks like a great PR! I'm on a train so I can't do a detailed review yet, but on the surface it looks great. Thanks for updating the tutorial and including tests as well! 🐈

@adelbertc
Copy link
Contributor

This is awesome, thanks! Looks like there is a legitimate build failure - I'm guessing Option#contains was added in 2.11 and missing from 2.10 :/

Other than that, lots of tests, lots of doc, I like it 👍

implicit def coproductSyntax[F[_], A](a: F[A]): CoproductOps[F, A] = new CoproductOps(a)
}

class CoproductOps[F[_], A](val a: F[A]) extends AnyVal {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: let's go ahead and make this class final to be consistent with other Ops classes.

@ceedubs
Copy link
Contributor

ceedubs commented Nov 17, 2015

Thanks a bunch @raulraja. I left some minor comments, but in general this is very clean, has good test coverage, and even comes with a great tutorial! 💯

@codecov-io
Copy link

Current coverage is 81.71%

Merging #671 into master will increase coverage by +0.74% as of 0ba18cd

@@            master    #671   diff @@
======================================
  Files          159     162     +3
  Stmts         2123    2198    +75
  Branches        72      72       
  Methods          0       0       
======================================
+ Hit           1719    1796    +77
  Partial          0       0       
+ Missed         404     402     -2

Review entire Coverage Diff as of 0ba18cd

Powered by Codecov. Updated on successful CI builds.

@raulraja
Copy link
Contributor Author

@ceedubs @adelbertc @non addressed all your comments, let me know if anything else can be improved. thx!


}

sealed abstract class CoproductInstances3 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the minor details, but could you make the CodproductInstances* traits private[data]? That will make them consistent with the work done in #612.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, I want to get it right so I understand what the expectations are for future contributions.:) Added those access modifiers also on InjectInstances

@raulraja
Copy link
Contributor Author

@ceedubs @adelbertc @non Added Free.inject[F[_], G[_]], NaturalTransformation.or with tests and CoflatMap and Foldable tests for Coproduct

checkAll("Coproduct[Option, Option, ?]", TraverseTests[Coproduct[Option, Option, ?]].traverse[Int, Int, Int, Int, Option, Option])
checkAll("Traverse[Coproduct[Option, Option, ?]]", SerializableTests.serializable(Traverse[Coproduct[Option, Option, ?]]))

checkAll("Coproduct[Option, Option, ?]", FoldableTests[Coproduct[Option, Option, ?]].foldable[Int, Int])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, I think this will pick up the same instances as the .traverse check above. That's why I recommended the somewhat bizarre ListWrapper.foldable approach in another comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I'll give that a shot

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ceedubs Would this do it? non@5ea5313

…ope so they don't pick up those from Traverse and Comonad
@ceedubs
Copy link
Contributor

ceedubs commented Nov 18, 2015

👍 💯

@non
Copy link
Contributor

non commented Nov 19, 2015

👍 Looks good to me!

@adelbertc
Copy link
Contributor

👍 Thanks @raulraja , awesome PR

adelbertc added a commit that referenced this pull request Nov 19, 2015
Request to Include `cats.data.Coproduct` and `cats.free.Inject`
@adelbertc adelbertc merged commit 29ffa90 into typelevel:master Nov 19, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants