-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 distributive typeclass and some instances #2046
Changes from 3 commits
a87b82b
30312b1
5d14de2
1f3962c
76915e6
0735388
87db30d
8499505
d646d83
85d24a2
fc59e26
b5daed3
7a2408a
b686896
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package cats | ||
import simulacrum.typeclass | ||
|
||
@typeclass trait Distributive[F[_]] extends Functor[F] { self => | ||
|
||
/** | ||
* Given a function which returns a distributive `F`, apply that value across the structure G. | ||
*/ | ||
def distribute[G[_]: Functor, A, B](ga: G[A])(f: A => F[B]): F[G[B]] | ||
|
||
/** | ||
* Given a Functor G which wraps some distributive F, distribute F across the G. | ||
*/ | ||
def cosequence[G[_]: Functor, A](ga: G[F[A]]): F[G[A]] = distribute(ga)(identity) | ||
|
||
// Distributive composes | ||
def compose[G[_]](implicit G0: Distributive[G]): Distributive[λ[α => F[G[α]]]] = | ||
new ComposedDistributive[F, G] { | ||
implicit def F = self | ||
implicit def G = G0 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package cats | ||
package syntax | ||
|
||
import cats.evidence.=== | ||
|
||
trait DistributiveSyntax extends Distributive.ToDistributiveOps { | ||
implicit final def catsSyntaxDistributiveOps[F[_]: Functor, A](fa: F[A]): DistributiveOps[F, A] = new DistributiveOps[F, A](fa) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should add another class for final class CosequenceOps[F[_]: Functor, G[_]: Distributive, A](val fga: F[G[A]]) extends AnyVal {
def cosequence: G[F[A]] = G.cosequence(fga)
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm, not sure I see a reason why that's better? The evidence constraint is on just the one method and it saves the extra class for syntax. Is there a reason it should be preferred? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe you're right, disregard me! :D |
||
} | ||
|
||
// Add syntax to functor as part of importing distributive syntax. | ||
final class DistributiveOps[F[_]: Functor, A](val fa: F[A]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we make this an AnyVal? :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, because the class has the Functor constraint. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes, you're right! We should move the |
||
def distribute[G[_], B](f: A => G[B])(implicit G: Distributive[G]): G[F[B]] = G.distribute(fa)(f) | ||
def cosequence[G[_], B](implicit G: Distributive[G], ev: A === G[B]): G[F[B]] = G.cosequence(ev.substitute(fa)) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package cats | ||
package laws | ||
|
||
import cats.Id | ||
trait DistributiveLaws[F[_]] extends FunctorLaws[F] { | ||
implicit override def F: Distributive[F] | ||
|
||
def cosequenceIdentity[G[_], A](fa: F[A]): IsEq[F[A]] = { | ||
F.cosequence[Id, A](fa) <-> fa | ||
} | ||
|
||
// TODO need laws for | ||
// cosequence andThen cosequence == id | ||
// composition | ||
} | ||
|
||
object DistributiveLaws { | ||
def apply[F[_]](implicit ev: Distributive[F]): DistributiveLaws[F] = | ||
new DistributiveLaws[F] { def F: Distributive[F] = ev } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package cats | ||
package laws | ||
package discipline | ||
|
||
trait DistributiveTests[F[_]] extends FunctorTests[F] { | ||
def laws: DistributiveLaws[F] | ||
} | ||
|
||
object DistributiveTests { | ||
def apply[F[_]: Distributive]: DistributiveTests[F] = | ||
new DistributiveTests[F] { def laws: DistributiveLaws[F] = DistributiveLaws[F] } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Cats convention is that instances that are more specific should have higher priority. So we should probably swap the
Functor
andDistributive
instance here.Also, according to naming convention of implicits, it should be
catsDataDistributiveForKleisli
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok.