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

Add ContravariantMonoidal #2034

Merged
merged 14 commits into from
Dec 1, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions core/src/main/scala/cats/Composed.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,17 @@ private[cats] trait ComposedContravariantCovariant[F[_], G[_]] extends Contravar
F.contramap(fga)(gb => G.map(gb)(f))
}

private[cats] trait ComposedApplicativeDivisible[F[_], G[_]] extends Divisible[λ[α => F[G[α]]]] { outer =>
private[cats] trait ComposedApplicativeContravariantMonoidal[F[_], G[_]] extends ContravariantMonoidal[λ[α => F[G[α]]]] { outer =>
Copy link
Contributor

@julienrf julienrf Nov 23, 2017

Choose a reason for hiding this comment

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

I wouldn’t repeat Monoidal here because Applicative already implies Monoidal since they are equivalent (see also this detailed explanation: https://stackoverflow.com/questions/23316255/lax-monoidal-functors-with-a-different-monoidal-structure).

Edit: sorry I was wrong. This trait composes a covariant applicative with a contravariant one, so the naming is correct, you can ignore my 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.

The SO was a good read, anyway, it clarified some of the points on different Monoidal structures for me, so thank you.

def F: Applicative[F]
def G: Divisible[G]
def G: ContravariantMonoidal[G]

override def unit[A]: F[G[A]] = F.pure(G.unit)

override def contramap2[A, B, C](fb: F[G[B]], fc: F[G[C]])(f: A => (B, C)): F[G[A]] =
F.ap2(F.pure((gb: G[B], gc: G[C]) => G.contramap2(gb, gc)(f)))(fb, fc)
override def contramap[A, B](fa: F[G[A]])(f: B => A) =
Copy link
Member

Choose a reason for hiding this comment

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

Can you add type annotations here and for product? :)

F.map(fa)(G.contramap(_)(f))

override def product[A, B](fa: F[G[A]], fb: F[G[B]]) =
F.map2(fa, fb)(G.product(_,_))
Copy link
Member

Choose a reason for hiding this comment

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

We need a space after the comma here :)

}

private[cats] trait ComposedSemigroupal[F[_], G[_]] extends ContravariantSemigroupal[λ[α => F[G[α]]]] with ComposedContravariantCovariant[F, G] { outer =>
Expand Down
32 changes: 32 additions & 0 deletions core/src/main/scala/cats/ContravariantMonoidal.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cats

import simulacrum.typeclass

/**
* ContravariantMonoidal functors
*
* Must obey the laws defined in cats.laws.ContravariantMonoidalLaws.
*
* Based on ekmett's contravariant library:
* https://hackage.haskell.org/package/contravariant-1.4/docs/Data-Functor-Contravariant-Divisible.html
*/
@typeclass trait ContravariantMonoidal[F[_]] extends ContravariantSemigroupal[F] { self =>

/**
* `unit` produces an instance of `F` for any type `A`
* that is trivial with respect to `contramap2` along
* the diagonal
*/
def unit[A]: F[A]

def liftContravariant[A, B](f: A => B): F[B] => F[A] =
Copy link
Contributor

Choose a reason for hiding this comment

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

This can actually be moved to Contravariant, no? (as it's just a curried contramap)

contramap2(unit, _: F[B])(((b: B) => (b, b)) compose f)

// Technically, this is not correct, as the Applicative is composed with the ContravariantMonoidal, not the other way around
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind elaborate a bit more (or point to a more detailed discussion/explanation) here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I can also just change the name and move this possibly. The point here is mostly that I didn't want to add anything to Applicative about ContravariantMonoidal since this might live in cats.more, so I ended up having this composition live here in ContravariantMonoidal even though the composition is Applicative compose ContravariantMonoidal, so the name and the signature are a little out of sync with what I saw in the rest of the repository.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, I propose we rename and move it to Applicative

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, will do. Thanks.

def composeApplicative[G[_]: Applicative]: ContravariantMonoidal[λ[α => G[F[α]]]] =
new ComposedApplicativeContravariantMonoidal[G, F] {
val F = Applicative[G]
val G = self
}
}

3 changes: 3 additions & 0 deletions core/src/main/scala/cats/ContravariantSemigroupal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ import simulacrum.typeclass
def F = self
def G = Functor[G]
}

def contramap2[A, B, C](fb: F[B], fc: F[C])(f: A => (B, C)): F[A] =
Copy link
Member

Choose a reason for hiding this comment

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

I'm not exactly sure if we need this definition here, as we already have contramap2- contramap22 in the auto-generated SemigroupalArityFunctions. WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

We could mix in the SemigroupalArityFunctions into the companion object of ContravariantSemigroupal maybe?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, yes! I agree. I didn't realize they were in SemigroupalArityFunctions. I'll do that.

contramap(product(fb, fc))(f)
}
52 changes: 0 additions & 52 deletions core/src/main/scala/cats/Divisible.scala

This file was deleted.

8 changes: 5 additions & 3 deletions core/src/main/scala/cats/data/Const.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ private[data] sealed abstract class ConstInstances extends ConstInstances0 {
fa.retag[B]
}

implicit def catsDataDivisibleForConst[D: Monoid]: Divisible[Const[D, ?]] = new Divisible[Const[D, ?]] {
implicit def catsDataContravariantMonoidalForConst[D: Monoid]: ContravariantMonoidal[Const[D, ?]] = new ContravariantMonoidal[Const[D, ?]] {
override def unit[A] = Const.empty[D, A]
override def contramap2[A, B, C](fb: Const[D, B], fc: Const[D, C])(f: A => (B, C)): Const[D, A] =
fb.retag[A] combine fc.retag[A]
override def contramap[A, B](fa: Const[D, A])(f: B => A): Const[D, B] =
fa.retag[B]
override def product[A, B](fa: Const[D, A], fb: Const[D, B]): Const[D, (A, B)] =
fa.retag[(A, B)] combine fb.retag[(A, B)]
}

implicit def catsDataTraverseForConst[C]: Traverse[Const[C, ?]] = new Traverse[Const[C, ?]] {
Expand Down
15 changes: 9 additions & 6 deletions core/src/main/scala/cats/data/IdT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,16 @@ private[data] sealed trait IdTApplicative[F[_]] extends Applicative[IdT[F, ?]] w
def pure[A](a: A): IdT[F, A] = IdT.pure(a)
}

private[data] sealed trait IdTDivisible[F[_]] extends Divisible[IdT[F, ?]] {
implicit val F0: Divisible[F]
private[data] sealed trait IdTContravariantMonoidal[F[_]] extends ContravariantMonoidal[IdT[F, ?]] {
implicit val F0: ContravariantMonoidal[F]

override def unit[A]: IdT[F, A] = IdT(F0.unit[A])

override def contramap2[A, B, C](fb: IdT[F, B], fc: IdT[F, C])(f: A => (B, C)): IdT[F, A] =
IdT(F0.contramap2(fb.value, fc.value)(f))
override def contramap[A, B](fa: IdT[F, A])(f: B => A): IdT[F, B] =
IdT(F0.contramap(fa.value)(f))

override def product[A, B](fa: IdT[F, A], fb: IdT[F, B]): IdT[F, (A, B)] =
IdT(F0.product(fa.value, fb.value))
}

private[data] sealed trait IdTFlatMap[F[_]] extends FlatMap[IdT[F, ?]] with IdTApply[F] {
Expand Down Expand Up @@ -133,8 +136,8 @@ private[data] sealed trait IdTNonEmptyTraverse[F[_]] extends IdTTraverse[F] with
}

private[data] sealed abstract class IdTInstances6 {
implicit def catsDataDivisibleForIdT[F[_]](implicit F: Divisible[F]): Divisible[IdT[F, ?]] =
new IdTDivisible[F] { implicit val F0: Divisible[F] = F }
implicit def catsDataContravariantMonoidalForIdT[F[_]](implicit F: ContravariantMonoidal[F]): ContravariantMonoidal[IdT[F, ?]] =
new IdTContravariantMonoidal[F] { implicit val F0: ContravariantMonoidal[F] = F }
}

private[data] sealed abstract class IdTInstances5 extends IdTInstances6 {
Expand Down
16 changes: 11 additions & 5 deletions core/src/main/scala/cats/data/IndexedStateT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,9 @@ private[data] sealed abstract class IndexedStateTInstances extends IndexedStateT
FA: Alternative[F]): Alternative[IndexedStateT[F, S, S, ?]] with Monad[IndexedStateT[F, S, S, ?]] =
new IndexedStateTAlternative[F, S] { implicit def F = FM; implicit def G = FA }

implicit def catsDataDivisibleForIndexedStateT[F[_], S](implicit FD: Divisible[F],
FA: Applicative[F]): Divisible[IndexedStateT[F, S, S, ?]] =
new IndexedStateTDivisible[F, S] { implicit def F = FD; implicit def G = FA }
implicit def catsDataContravariantMonoidalForIndexedStateT[F[_], S](implicit FD: ContravariantMonoidal[F],
FA: Applicative[F]): ContravariantMonoidal[IndexedStateT[F, S, S, ?]] =
new IndexedStateTContravariantMonoidal[F, S] { implicit def F = FD; implicit def G = FA }
}

private[data] sealed abstract class IndexedStateTInstances1 extends IndexedStateTInstances2 {
Expand Down Expand Up @@ -366,13 +366,19 @@ private[data] sealed abstract class IndexedStateTSemigroupK[F[_], SA, SB] extend
IndexedStateT(s => G.combineK(x.run(s), y.run(s)))
}

private[data] sealed abstract class IndexedStateTDivisible[F[_], S] extends Divisible[IndexedStateT[F, S, S, ?]]{
implicit def F: Divisible[F]
private[data] sealed abstract class IndexedStateTContravariantMonoidal[F[_], S] extends ContravariantMonoidal[IndexedStateT[F, S, S, ?]]{
implicit def F: ContravariantMonoidal[F]
implicit def G: Applicative[F]

override def unit[A]: IndexedStateT[F, S, S, A] =
IndexedStateT.applyF(G.pure((s: S) => F.unit[(S, A)]))

override def contramap[A, B](fa: IndexedStateT[F, S, S, A])(f: B => A): IndexedStateT[F, S, S, B] =
contramap2(fa, unit)(((a: A) => (a, a)) compose f)

override def product[A, B](fa: IndexedStateT[F, S, S, A], fb: IndexedStateT[F, S, S, B]): IndexedStateT[F, S, S, (A, B)] =
contramap2(fa, fb)(identity)

override def contramap2[A, B, C](fb: IndexedStateT[F, S, S, B], fc: IndexedStateT[F, S, S, C])(f: A => (B, C)): IndexedStateT[F, S, S, A] =
IndexedStateT.applyF(
G.pure((s: S) =>
Expand Down
15 changes: 9 additions & 6 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3
implicit def catsDataAlternativeForKleisli[F[_], A](implicit F0: Alternative[F]): Alternative[Kleisli[F, A, ?]] =
new KleisliAlternative[F, A] { def F: Alternative[F] = F0 }

implicit def catsDataDivisibleForKleisli[F[_], A](implicit F0: Divisible[F]): Divisible[Kleisli[F, A, ?]] =
new KleisliDivisible[F, A] { def F: Divisible[F] = F0 }
implicit def catsDataContravariantMonoidalForKleisli[F[_], A](implicit F0: ContravariantMonoidal[F]): ContravariantMonoidal[Kleisli[F, A, ?]] =
Copy link
Contributor

Choose a reason for hiding this comment

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

The convention in cats is that instances that are more specific should be at higher priority. I.E. instance of ContravariantMonoidal should be lower in the inheritance chain than Contravariant and ContravriantSemigroupal and Semigroupal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gotcha, will fix.

new KleisliContravariantMonoidal[F, A] { def F: ContravariantMonoidal[F] = F0 }
}

private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4 {
Expand Down Expand Up @@ -297,13 +297,16 @@ private[data] trait KleisliAlternative[F[_], A] extends Alternative[Kleisli[F, A
implicit def F: Alternative[F]
}

private[data] sealed trait KleisliDivisible[F[_], D] extends Divisible[Kleisli[F, D, ?]] {
implicit def F: Divisible[F]
private[data] sealed trait KleisliContravariantMonoidal[F[_], D] extends ContravariantMonoidal[Kleisli[F, D, ?]] {
implicit def F: ContravariantMonoidal[F]

override def unit[A]: Kleisli[F, D, A] = Kleisli(Function.const(F.unit[A]))

override def contramap2[A, B, C](fb: Kleisli[F, D, B], fc: Kleisli[F, D, C])(f: A => (B, C)): Kleisli[F, D, A] =
Kleisli(d => F.contramap2(fb.run(d), fc.run(d))(f))
override def contramap[A, B](fa: Kleisli[F, D, A])(f: B => A): Kleisli[F, D, B] =
Kleisli(d => F.contramap(fa.run(d))(f))

override def product[A, B](fa: Kleisli[F, D, A], fb: Kleisli[F, D, B]): Kleisli[F, D, (A, B)] =
Kleisli(d => F.product(fa.run(d), fb.run(d)))
}

private[data] trait KleisliMonadError[F[_], A, E] extends MonadError[Kleisli[F, A, ?], E] with KleisliApplicativeError[F, A, E] with KleisliMonad[F, A] {
Expand Down
17 changes: 10 additions & 7 deletions core/src/main/scala/cats/data/Nested.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ private[data] sealed abstract class NestedInstances1 extends NestedInstances2 {
val FG: Functor[λ[α => F[G[α]]]] = Contravariant[F].compose[G]
}

implicit def catsDataDivisibleForApplicativeForNested[F[_]: Applicative, G[_]: Divisible]: Divisible[Nested[F, G, ?]] =
new NestedDivisible[F, G] {
val FG: Divisible[λ[α => F[G[α]]]] = Divisible[G].composeApplicative[F]
implicit def catsDataContravariantMonoidalForApplicativeForNested[F[_]: Applicative, G[_]: ContravariantMonoidal]: ContravariantMonoidal[Nested[F, G, ?]] =
new NestedContravariantMonoidal[F, G] {
val FG: ContravariantMonoidal[λ[α => F[G[α]]]] = ContravariantMonoidal[G].composeApplicative[F]
}
}

Expand Down Expand Up @@ -269,11 +269,14 @@ private[data] trait NestedContravariant[F[_], G[_]] extends Contravariant[Nested
Nested(FG.contramap(fga.value)(f))
}

private[data] trait NestedDivisible[F[_], G[_]] extends Divisible[Nested[F, G, ?]] {
def FG: Divisible[λ[α => F[G[α]]]]
private[data] trait NestedContravariantMonoidal[F[_], G[_]] extends ContravariantMonoidal[Nested[F, G, ?]] {
def FG: ContravariantMonoidal[λ[α => F[G[α]]]]

def unit[A]: Nested[F, G, A] = Nested(FG.unit)

def contramap2[A, B, C](fb: Nested[F, G, B], fc: Nested[F, G, C])(f: A => (B, C)): Nested[F, G, A] =
Nested(FG.contramap2(fb.value, fc.value)(f))
def contramap[A, B](fa: Nested[F, G, A])(f: B => A): Nested[F, G, B] =
Nested(FG.contramap(fa.value)(f))

def product[A, B](fa: Nested[F, G, A], fb: Nested[F, G, B]): Nested[F, G, (A, B)] =
Nested(FG.product(fa.value, fb.value))
}
21 changes: 14 additions & 7 deletions core/src/main/scala/cats/data/OptionT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ private[data] sealed abstract class OptionTInstances0 extends OptionTInstances1
implicit def catsDataMonadErrorForOptionT[F[_], E](implicit F0: MonadError[F, E]): MonadError[OptionT[F, ?], E] =
new OptionTMonadError[F, E] { implicit val F = F0 }

implicit def catsDataDivisibleForOptionT[F[_]](implicit F0: Divisible[F]): Divisible[OptionT[F, ?]] =
new OptionTDivisible[F] { implicit val F = F0 }
implicit def catsDataContravariantMonoidalForOptionT[F[_]](implicit F0: ContravariantMonoidal[F]): ContravariantMonoidal[OptionT[F, ?]] =
new OptionTContravariantMonoidal[F] { implicit val F = F0 }

implicit def catsDataSemigroupKForOptionT[F[_]](implicit F0: Monad[F]): SemigroupK[OptionT[F, ?]] =
new OptionTSemigroupK[F] { implicit val F = F0 }
Expand Down Expand Up @@ -284,14 +284,21 @@ private trait OptionTMonadError[F[_], E] extends MonadError[OptionT[F, ?], E] wi
OptionT(F.handleErrorWith(fa.value)(f(_).value))
}

private trait OptionTDivisible[F[_]] extends Divisible[OptionT[F, ?]] {
def F: Divisible[F]
private trait OptionTContravariantMonoidal[F[_]] extends ContravariantMonoidal[OptionT[F, ?]] {
def F: ContravariantMonoidal[F]

override def unit[A]: OptionT[F, A] = OptionT (F.unit)

// Really this wants to be written in terms of arrow :/
override def contramap2[A, B, C](fb: OptionT[F, B], fc: OptionT[F, C])(f: A => (B, C)): OptionT[F, A] =
OptionT(F.contramap2(fb.value, fc.value)(x => (x.map(f andThen (_._1)), x.map(f andThen (_._2)))))
override def contramap[A, B](fa: OptionT[F, A])(f: B => A): OptionT[F, B] =
OptionT(F.contramap(fa.value)(_ map f))

override def product[A, B](fa: OptionT[F, A], fb: OptionT[F, B]): OptionT[F, (A, B)] =
OptionT(F.contramap(F.product(fa.value, fb.value))(
(t: Option[(A, B)]) => t match {
case Some((x, y)) => (Some(x), Some(y))
case None => (None, None)
}
))
}

private[data] trait OptionTFoldable[F[_]] extends Foldable[OptionT[F, ?]] {
Expand Down
20 changes: 11 additions & 9 deletions core/src/main/scala/cats/data/Tuple2K.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ private[data] sealed abstract class Tuple2KInstances extends Tuple2KInstances0 {
def F: Contravariant[F] = FC
def G: Contravariant[G] = GC
}
implicit def catsDataDivisibleForTuple2k[F[_], G[_]](implicit FD: Divisible[F], GD: Divisible[G]): Divisible[λ[α => Tuple2K[F, G, α]]] =
new Tuple2KDivisible[F, G] {
def F: Divisible[F] = FD
def G: Divisible[G] = GD
implicit def catsDataContravariantMonoidalForTuple2k[F[_], G[_]](implicit FD: ContravariantMonoidal[F], GD: ContravariantMonoidal[G]): ContravariantMonoidal[λ[α => Tuple2K[F, G, α]]] =
new Tuple2KContravariantMonoidal[F, G] {
def F: ContravariantMonoidal[F] = FD
def G: ContravariantMonoidal[G] = GD
}
}

Expand Down Expand Up @@ -128,12 +128,14 @@ private[data] sealed trait Tuple2KContravariant[F[_], G[_]] extends Contravarian
def contramap[A, B](fa: Tuple2K[F, G, A])(f: B => A): Tuple2K[F, G, B] = Tuple2K(F.contramap(fa.first)(f), G.contramap(fa.second)(f))
}

private[data] sealed trait Tuple2KDivisible[F[_], G[_]] extends Divisible[λ[α => Tuple2K[F, G, α]]] {
def F: Divisible[F]
def G: Divisible[G]
private[data] sealed trait Tuple2KContravariantMonoidal[F[_], G[_]] extends ContravariantMonoidal[λ[α => Tuple2K[F, G, α]]] {
def F: ContravariantMonoidal[F]
def G: ContravariantMonoidal[G]
def unit[A]: Tuple2K[F, G, A] = Tuple2K(F.unit, G.unit)
def contramap2[A, B, C](fb: Tuple2K[F, G, B], fc: Tuple2K[F, G, C])(f: A => (B, C)): Tuple2K[F, G, A] =
Tuple2K(F.contramap2(fb.first, fc.first)(f), G.contramap2(fb.second, fc.second)(f))
def product[A, B](fa: Tuple2K[F, G, A], fb: Tuple2K[F, G, B]): Tuple2K[F, G, (A, B)] =
Tuple2K(F.product(fa.first, fb.first), G.product(fa.second, fb.second))
def contramap[A, B](fa: Tuple2K[F, G, A])(f: B => A): Tuple2K[F, G, B] =
Tuple2K(F.contramap(fa.first)(f), G.contramap(fa.second)(f))
}

private[data] sealed trait Tuple2KApply[F[_], G[_]] extends Apply[λ[α => Tuple2K[F, G, α]]] with Tuple2KFunctor[F, G] {
Expand Down
24 changes: 15 additions & 9 deletions core/src/main/scala/cats/data/WriterT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7
implicit val L0: Monoid[L] = L
}

implicit def catsDataDivisibleForWriterT[F[_], L](implicit F: Divisible[F]): Divisible[WriterT[F, L, ?]] =
new WriterTDivisible[F, L] {
implicit val F0: Divisible[F] = F
implicit def catsDataContravariantMonoidalForWriterT[F[_], L](implicit F: ContravariantMonoidal[F]): ContravariantMonoidal[WriterT[F, L, ?]] =
new WriterTContravariantMonoidal[F, L] {
implicit val F0: ContravariantMonoidal[F] = F
}
}

Expand Down Expand Up @@ -358,15 +358,21 @@ private[data] sealed trait WriterTAlternative[F[_], L] extends Alternative[Write
override implicit def F0: Alternative[F]
}

private[data] sealed trait WriterTDivisible[F[_], L] extends Divisible[WriterT[F, L, ?]] {
implicit def F0: Divisible[F]
private[data] sealed trait WriterTContravariantMonoidal[F[_], L] extends ContravariantMonoidal[WriterT[F, L, ?]] {
implicit def F0: ContravariantMonoidal[F]

override def unit[A]: WriterT[F, L, A] = WriterT(F0.unit[(L, A)])

override def contramap2[A, B, C](fb: WriterT[F, L, B], fc: WriterT[F, L, C])(f: A => (B, C)): WriterT[F, L, A] =
WriterT(F0.contramap2(fb.run, fc.run)((tup: (L, A)) => f(tup._2) match {
case (b, c) => ((tup._1, b), (tup._1, c))
}))
override def contramap[A, B](fa: WriterT[F, L, A])(f: B => A): WriterT[F, L, B] =
WriterT(F0.contramap(fa.run)((d: (L, B)) => (d._1, f(d._2))))

override def product[A, B](fa: WriterT[F, L, A], fb: WriterT[F, L, B]): WriterT[F, L, (A, B)] =
WriterT(
F0.contramap(
F0.product(fa.run, fb.run))(
(t: (L, (A, B))) => t match {
case (l, (a, b)) => ((l, a), (l, b))
}))
}

private[data] sealed trait WriterTSemigroup[F[_], L, A] extends Semigroup[WriterT[F, L, A]] {
Expand Down
Loading