-
-
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
added TFunctor
#1815
Closed
Closed
added TFunctor
#1815
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
6e843e3
added TFunctor
kailuowang c5f2874
fix format
kailuowang bfb0d8a
added TFunctor instance for Free
kailuowang d9d4f5a
added doctest example for liftNT
kailuowang 9621b9b
added instances for Tuple2K and EitherK
kailuowang 1d8f33a
added instances for Nested, Func and OneAnd
kailuowang 3f0f79d
fix typo
kailuowang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package cats | ||
|
||
import simulacrum.typeclass | ||
|
||
/** | ||
* This is an endofunctor in the category of endofunctors in `Skal`. | ||
* | ||
* `Skal` is the category of scala types. Functors in `Skal` | ||
* is encoded as `Functor`. The functors in `Skal` themselves forms | ||
* a category, let's denote it as `F[Skal]`. | ||
* In `F[Skal]`, functors of Skal, e.g. `Option[_]` and `Either[E, _]`, are objects, | ||
* while natural transformations, e.g. `Option ~> Either[E, ?]`, are arrows. | ||
* A endofunctor in `F[Skal]` maps one set of functors of `Skal` to another | ||
* set of functors of `Skal` while preserving the structures. | ||
* | ||
* For `TFunctor`, the domain is `F[_]`, the codomain is `T[F, _]`, both are | ||
* functors in `Skal`. The `TFunctor` provides a mapping from the arrows between | ||
* `F[_]` and `G[_]`, i.e. `F ~> G` to arrows between `T[F, _]` and `T[G, _]`, | ||
* i.e. `T[F, ?] ~> T[G, ?]`. The `lift` method makes this intention clear. | ||
* | ||
* In `cats.core`, examples of such `TFunctor`s are monad transformers such | ||
* as `OptionT`, `EitherT` | ||
* | ||
*/ | ||
@typeclass trait TFunctor[T[_[_], _]] { | ||
def mapNT[F[_], G[_], A](h: T[F, A])(f: F ~> G): T[G, A] | ||
|
||
/** | ||
* Lift a `F ~> G` to a `T[F, ?] ~> T[G, ?]`. | ||
* | ||
* {{{ | ||
* scala> import cats.implicits._, cats.data.OptionT | ||
* scala> val lv: List ~> Vector = λ[List ~> Vector](_.toVector) | ||
* scala> val olv: OptionT[List, ?] ~> OptionT[Vector, ?] = TFunctor[OptionT].liftNT(lv) | ||
* scala> olv(OptionT.liftF(List(1))) | ||
* res0: OptionT[Vector, Int] = OptionT(Vector(Some(1))) | ||
* }}} | ||
*/ | ||
def liftNT[F[_], G[_]](f: F ~> G): T[F, ?] ~> T[G, ?] = | ||
λ[T[F, ?] ~> T[G, ?]](hf => mapNT(hf)(f)) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package cats | ||
package syntax | ||
|
||
trait TFunctorSyntax extends TFunctor.ToTFunctorOps |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package cats | ||
package laws | ||
|
||
|
||
import cats.arrow.FunctionK | ||
import syntax.all._ | ||
import cats.~> | ||
|
||
trait TFunctorLaws[T[_[_], _]]{ | ||
implicit def T: TFunctor[T] | ||
|
||
def covariantIdentity[F[_], A](fg: T[F, A]): IsEq[T[F, A]] = | ||
fg.mapNT(FunctionK.id[F]) <-> fg | ||
|
||
def covariantComposition[F[_], G[_], H[_], A](fa: T[F, A], f: F ~> G, g: G ~> H): IsEq[T[H, A]] = | ||
fa.mapNT(f).mapNT(g) <-> fa.mapNT(f andThen g) | ||
|
||
} | ||
|
||
object TFunctorLaws { | ||
def apply[T[_[_], _]](implicit ev: TFunctor[T]): TFunctorLaws[T] = | ||
new TFunctorLaws[T] { def T: TFunctor[T] = ev } | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
laws/src/main/scala/cats/laws/discipline/TFunctorTests.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package cats | ||
package laws | ||
package discipline | ||
|
||
|
||
import org.scalacheck.Arbitrary | ||
import org.scalacheck.Prop._ | ||
import cats.{Eq, ~>} | ||
import org.typelevel.discipline.Laws | ||
|
||
trait TFunctorTests[T[_[_], _]] extends Laws { | ||
def laws: TFunctorLaws[T] | ||
|
||
def tfunctor[F[_], G[_], H[_], A: Arbitrary](implicit | ||
ArbFA: Arbitrary[T[F, A]], | ||
ArbitraryG: Arbitrary[F[A]], | ||
ArbitraryH: Arbitrary[G[A]], | ||
ArbitraryI: Arbitrary[H[A]], | ||
ArbitraryFK: Arbitrary[F ~> G], | ||
ArbitraryFK2: Arbitrary[G ~> H], | ||
ArbitraryFK3: Arbitrary[G ~> F], | ||
ArbitraryFK4: Arbitrary[H ~> G], | ||
EqFA: Eq[T[F, A]], | ||
EqFC: Eq[T[H, A]] | ||
): RuleSet = { | ||
new DefaultRuleSet( | ||
name = "TFunctor", | ||
parent = None, | ||
"covariant identity" -> forAll(laws.covariantIdentity[F, A] _), | ||
"covariant composition" -> forAll(laws.covariantComposition[F, G, H, A] _)) | ||
} | ||
} | ||
|
||
object TFunctorTests { | ||
def apply[T[_[_], _]: TFunctor]: TFunctorTests[T] = | ||
new TFunctorTests[T] { def laws: TFunctorLaws[T] = TFunctorLaws[T] } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
do we need
T[_[_], _]
or wouldT[_[_]]
do? I'm not sure what the second parameter is buying us. Maybe I'm missing it. Can't we just ignore the inner value type? Also, wouldn't we have more flexibility only acting onT[_[_]]
?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.
Agreed here. If we had
T[_[_]]
I could come up with a lot more usecases. Maybe call itFunctorK
then. We'd have to wrap the transformers inForall
though, which may mean our unified transform syntax fails to be found implicitly.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 main reason I chose
T[_[_], _]
here, is that given anyFunctor
F[_]
,T[F, _]
is also aFunctor
. SoT[_[_], _]
can be deemed as a functor between theF[_]
Functor
and theT[F, _]
Functor
. i.e. a endofunctor in the category of endofunctors, thus more like a higher kindedFunctor
.If you use
T[_[_]]
, then given anyFunctor
F[_]
,T[F]
isn't aFunctor
, it's a kind * type, not a type constructor. SoT[_[_]]
is a functor betweenF[_]
Functor
and kind * typeT[F]
s. That makes it less a high kindedFunctor
. However, you can work around with the parametricity in instance definitions, e.g. forOptionT
,With parametric
A
, we can have aFunctorK
instance forallA
s. In some sense, we may choose to see this set ofFunctorK
instances as a single truely higher kinded functor.Practical use wise, I can't think of any downside of using
T[_[_]]
. The only downside is its awkward encoding of the higher kindedFunctor
- it relies on instance definition. It may have more usecases, one that immediately jumps out isFunctionK
. So maybe practically it's more useful.Now, the fact that we aren't sure about this thing makes me think that maybe we shouldn't place this in
cats-core
. I already haveFunctorK[T[_]]
defined in mainecoon, I could add all these instances there. Or we can create a newcats-extra
module for this type of less commonly used type class.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.
With the example of
FunctionK
, my first thought is that that’s what’s defined incorrectly. We really wantFunctionK[F[_], G[_], A]
andForall[FunctionK[F, G, ?]]
for a natural transformation. Then you do have aFunctorK[FunctionK[F, ?[_], ?]]
. But that might just be crazy talk.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.
I'm not sure if "higher-kinded X" is a really rigid term. In my mind, higher-kinded functor could equally well mean a functor from (endofunctors in Scal) to Scal, or a functor from endofunctors in Scal to other endofunctors in Scal. This is my main problem with the
-K
naming scheme; it gets even worse when type classes have multiple type parameters.As well, the encoding proposed above does not reify that the
A
can vary with the instance remaining valid. The consumer of the instance needs aForall[Lambda[A =>FunctorK[OptionT[?[_], A]]]]
to have that fact in hand, and that makes it much more unwieldy, though a type alias could help.To my mind if we're going to do a cats-extras project we'll want this there.
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.
I agree with @kailuowang and @edmundnoble, I don't think it's all that useful in cats-core. I'd like to see conformity in Monad Transformers for defining a
mapK
method, but it doesn't need a type class in core IMO.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.
Absolutely. I don’t think it helps at all. But if
Functor
is really “endofunctor in the category of Scal”, I’d expectFunctorK
to mean the same thing asK
in other instances, which basically adds “endofunctors” to the description – i.e., “endofunctor in the category of endofunctors in Scal”.I’d also be happy with
Functor
being renamed toEndofunctor
and havingEndofunctorK
and something likeLowerFunctor
(for a functor from the category of endofunctors to the category of Skal).Also agreed that none of this should be in cats-core. I would like to find some time to work with
-Ykind-polymorphism
and to define these things as specializations of kind-polymorphic endofunctors, etc.This sort of thing has been started in a few different libraries already – khats (which is too homophonically-named to be useful), Griffins (which never really got off the ground and mostly lives inside a Matryoshka branch), and I think one other that I have forgotten 😕