Skip to content

Commit 00e5ab4

Browse files
committed
Generalize ApplyBuilder to Monoidal.
1 parent cb804ce commit 00e5ab4

21 files changed

Lines changed: 223 additions & 70 deletions

File tree

core/src/main/scala/cats/Applicative.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ import simulacrum.typeclass
2828
*/
2929
def pureEval[A](x: Eval[A]): F[A] = pure(x.value)
3030

31-
override def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(pure(f))
32-
3331
/**
3432
* Two sequentially dependent Applicatives can be composed.
3533
*

core/src/main/scala/cats/Apply.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,27 @@ import simulacrum.typeclass
88
* Must obey the laws defined in cats.laws.ApplyLaws.
99
*/
1010
@typeclass(excludeParents=List("ApplyArityFunctions"))
11-
trait Apply[F[_]] extends Functor[F] with ApplyArityFunctions[F] { self =>
11+
trait Apply[F[_]] extends Functor[F] with Monoidal[F] with ApplyArityFunctions[F] { self =>
1212

1313
/**
1414
* Given a value and a function in the Apply context, applies the
1515
* function to the value.
1616
*/
17-
def ap[A, B](fa: F[A])(f: F[A => B]): F[B]
17+
def ap[A, B](fa: F[A])(ff: F[A => B]): F[B]
1818

1919
/**
2020
* ap2 is a binary version of ap, defined in terms of ap.
2121
*/
22-
def ap2[A, B, Z](fa: F[A], fb: F[B])(f: F[(A, B) => Z]): F[Z] =
23-
ap(fb)(ap(fa)(map(f)(f => (a: A) => (b: B) => f(a, b))))
22+
def ap2[A, B, Z](fa: F[A], fb: F[B])(ff: F[(A, B) => Z]): F[Z] =
23+
map(product(fa, product(fb, ff))) { case (a, (b, f)) => f(a, b) }
2424

2525
/**
2626
* Applies the pure (binary) function f to the effectful values fa and fb.
2727
*
2828
* map2 can be seen as a binary version of [[cats.Functor]]#map.
2929
*/
3030
def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z): F[Z] =
31-
ap(fb)(map(fa)(a => (b: B) => f(a, b)))
31+
map(product(fa, fb)) { case (a, b) => f(a, b) }
3232

3333
/**
3434
* Two sequentially dependent Applys can be composed.
@@ -45,6 +45,7 @@ trait Apply[F[_]] extends Functor[F] with ApplyArityFunctions[F] { self =>
4545
def F: Apply[F] = self
4646
def G: Apply[G] = GG
4747
}
48+
4849
}
4950

5051
trait CompositeApply[F[_], G[_]]
@@ -54,4 +55,8 @@ trait CompositeApply[F[_], G[_]]
5455

5556
def ap[A, B](fa: F[G[A]])(f: F[G[A => B]]): F[G[B]] =
5657
F.ap(fa)(F.map(f)(gab => G.ap(_)(gab)))
58+
59+
def product[A, B](fa: F[G[A]], fb: F[G[B]]): F[G[(A, B)]] =
60+
F.map2(fa, fb)(G.product)
61+
5762
}

core/src/main/scala/cats/FlatMap.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ import simulacrum.typeclass
2929
override def ap[A, B](fa: F[A])(ff: F[A => B]): F[B] =
3030
flatMap(ff)(f => map(fa)(f))
3131

32+
override def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] =
33+
flatMap(fa)(a => map(fb)(b => (a, b)))
34+
3235
/**
3336
* Pair `A` with the result of function application.
3437
*/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package cats
2+
3+
import simulacrum.typeclass
4+
5+
/**
6+
* Monoidal allows us to express uncurried function application within a context,
7+
* whatever the context variance is.
8+
*
9+
* It is worth noting that the couple Monoidal and [[Functor]] is interdefinable with [[Apply]].
10+
*/
11+
@typeclass trait Monoidal[F[_]] {
12+
def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
13+
}
14+
15+
object Monoidal extends MonoidalArityFunctions

core/src/main/scala/cats/data/Const.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ sealed abstract class ConstInstances0 extends ConstInstances1 {
7676

7777
def ap[A, B](fa: Const[C, A])(f: Const[C, A => B]): Const[C, B] =
7878
f.retag[B] combine fa.retag[B]
79+
80+
def map[A, B](fa: Const[C, A])(f: A => B): Const[C, B] =
81+
fa.retag[B]
82+
83+
def product[A, B](fa: Const[C, A], fb: Const[C, B]): Const[C, (A, B)] =
84+
fa.retag[(A, B)] combine fb.retag[(A, B)]
7985
}
8086
}
8187

@@ -89,6 +95,9 @@ sealed abstract class ConstInstances1 {
8995
def ap[A, B](fa: Const[C, A])(f: Const[C, A => B]): Const[C, B] =
9096
fa.retag[B] combine f.retag[B]
9197

98+
def product[A, B](fa: Const[C, A], fb: Const[C, B]): Const[C, (A, B)] =
99+
fa.retag[(A, B)] combine fb.retag[(A, B)]
100+
92101
def map[A, B](fa: Const[C, A])(f: A => B): Const[C, B] =
93102
fa.retag[B]
94103
}

core/src/main/scala/cats/data/Func.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,21 @@ abstract class FuncInstances1 {
5454

5555
sealed trait FuncFunctor[F[_], C] extends Functor[Lambda[X => Func[F, C, X]]] {
5656
def F: Functor[F]
57-
override def map[A, B](fa: Func[F, C, A])(f: A => B): Func[F, C, B] =
57+
def map[A, B](fa: Func[F, C, A])(f: A => B): Func[F, C, B] =
5858
fa.map(f)(F)
5959
}
6060

6161
sealed trait FuncApply[F[_], C] extends Apply[Lambda[X => Func[F, C, X]]] with FuncFunctor[F, C] {
6262
def F: Apply[F]
63-
override def ap[A, B](fa: Func[F, C, A])(f: Func[F, C, A => B]): Func[F, C, B] =
63+
def ap[A, B](fa: Func[F, C, A])(f: Func[F, C, A => B]): Func[F, C, B] =
6464
Func.func(c => F.ap(fa.run(c))(f.run(c)))
65+
def product[A, B](fa: Func[F, C, A], fb: Func[F, C, B]): Func[F, C, (A, B)] =
66+
Func.func(c => F.product(fa.run(c), fb.run(c)))
6567
}
6668

6769
sealed trait FuncApplicative[F[_], C] extends Applicative[Lambda[X => Func[F, C, X]]] with FuncApply[F, C] {
6870
def F: Applicative[F]
69-
override def pure[A](a: A): Func[F, C, A] =
71+
def pure[A](a: A): Func[F, C, A] =
7072
Func.func(c => F.pure(a))
7173
}
7274

@@ -119,10 +121,12 @@ abstract class AppFuncInstances {
119121

120122
sealed trait AppFuncApplicative[F[_], C] extends Applicative[Lambda[X => AppFunc[F, C, X]]] {
121123
def F: Applicative[F]
122-
override def map[A, B](fa: AppFunc[F, C, A])(f: A => B): AppFunc[F, C, B] =
124+
def map[A, B](fa: AppFunc[F, C, A])(f: A => B): AppFunc[F, C, B] =
123125
fa.map(f)
124-
override def ap[A, B](fa: AppFunc[F, C, A])(f: AppFunc[F, C, A => B]): AppFunc[F, C, B] =
126+
def ap[A, B](fa: AppFunc[F, C, A])(f: AppFunc[F, C, A => B]): AppFunc[F, C, B] =
125127
Func.appFunc[F, C, B](c => F.ap(fa.run(c))(f.run(c)))(F)
126-
override def pure[A](a: A): AppFunc[F, C, A] =
128+
def product[A, B](fa: AppFunc[F, C, A], fb: AppFunc[F, C, B]): AppFunc[F, C, (A, B)] =
129+
Func.appFunc[F, C, (A, B)](c => F.product(fa.run(c), fb.run(c)))(F)
130+
def pure[A](a: A): AppFunc[F, C, A] =
127131
Func.appFunc[F, C, A](c => F.pure(a))(F)
128132
}

core/src/main/scala/cats/data/Kleisli.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ sealed abstract class KleisliInstances1 extends KleisliInstances2 {
147147

148148
def ap[B, C](fa: Kleisli[F, A, B])(f: Kleisli[F, A, B => C]): Kleisli[F, A, C] =
149149
fa(f)
150+
151+
def map[B, C](fb: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] =
152+
fb.map(f)
153+
154+
def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] =
155+
Kleisli.function(a => Applicative[F].product(fb.run(a), fc.run(a)))
150156
}
151157
}
152158

@@ -155,6 +161,9 @@ sealed abstract class KleisliInstances2 extends KleisliInstances3 {
155161
def ap[B, C](fa: Kleisli[F, A, B])(f: Kleisli[F, A, B => C]): Kleisli[F, A, C] =
156162
fa(f)
157163

164+
def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] =
165+
Kleisli.function(a => Apply[F].product(fb.run(a), fc.run(a)))
166+
158167
def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] =
159168
fa.map(f)
160169
}

core/src/main/scala/cats/data/Prod.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,20 +71,22 @@ sealed abstract class ProdInstance4 {
7171
sealed trait ProdFunctor[F[_], G[_]] extends Functor[Lambda[X => Prod[F, G, X]]] {
7272
def F: Functor[F]
7373
def G: Functor[G]
74-
override def map[A, B](fa: Prod[F, G, A])(f: A => B): Prod[F, G, B] = Prod(F.map(fa.first)(f), G.map(fa.second)(f))
74+
def map[A, B](fa: Prod[F, G, A])(f: A => B): Prod[F, G, B] = Prod(F.map(fa.first)(f), G.map(fa.second)(f))
7575
}
7676

7777
sealed trait ProdApply[F[_], G[_]] extends Apply[Lambda[X => Prod[F, G, X]]] with ProdFunctor[F, G] {
7878
def F: Apply[F]
7979
def G: Apply[G]
80-
override def ap[A, B](fa: Prod[F, G, A])(f: Prod[F, G, A => B]): Prod[F, G, B] =
80+
def ap[A, B](fa: Prod[F, G, A])(f: Prod[F, G, A => B]): Prod[F, G, B] =
8181
Prod(F.ap(fa.first)(f.first), G.ap(fa.second)(f.second))
82+
def product[A, B](fa: Prod[F, G, A], fb: Prod[F, G, B]): Prod[F, G, (A, B)] =
83+
Prod(F.product(fa.first, fb.first), G.product(fa.second, fb.second))
8284
}
8385

8486
sealed trait ProdApplicative[F[_], G[_]] extends Applicative[Lambda[X => Prod[F, G, X]]] with ProdApply[F, G] {
8587
def F: Applicative[F]
8688
def G: Applicative[G]
87-
override def pure[A](a: A): Prod[F, G, A] = Prod(F.pure(a), G.pure(a))
89+
def pure[A](a: A): Prod[F, G, A] = Prod(F.pure(a), G.pure(a))
8890
}
8991

9092
sealed trait ProdSemigroupK[F[_], G[_]] extends SemigroupK[Lambda[X => Prod[F, G, X]]] {

core/src/main/scala/cats/data/Validated.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,25 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable {
111111
b => that.fold(_ => false, AA.eqv(b, _))
112112
)
113113

114-
115114
/**
116115
* From Apply:
117116
* if both the function and this value are Valid, apply the function
118117
*/
119118
def ap[EE >: E, B](f: Validated[EE, A => B])(implicit EE: Semigroup[EE]): Validated[EE,B] =
120119
(this, f) match {
121120
case (Valid(a), Valid(f)) => Valid(f(a))
122-
case (Invalid(e1), Invalid(e2)) => Invalid(EE.combine(e2,e1))
121+
case (Invalid(e1), Invalid(e2)) => Invalid(EE.combine(e2, e1))
122+
case (e@Invalid(_), _) => e
123+
case (_, e@Invalid(_)) => e
124+
}
125+
126+
/**
127+
* From Product
128+
*/
129+
def product[EE >: E, B](fb: Validated[EE, B])(implicit EE: Semigroup[EE]): Validated[EE, (A, B)] =
130+
(this, fb) match {
131+
case (Valid(a), Valid(b)) => Valid((a, b))
132+
case (Invalid(e1), Invalid(e2)) => Invalid(EE.combine(e1, e2))
123133
case (e @ Invalid(_), _) => e
124134
case (_, e @ Invalid(_)) => e
125135
}
@@ -196,8 +206,11 @@ sealed abstract class ValidatedInstances extends ValidatedInstances1 {
196206
override def map[A, B](fa: Validated[E,A])(f: A => B): Validated[E, B] =
197207
fa.map(f)
198208

199-
override def ap[A,B](fa: Validated[E,A])(f: Validated[E,A=>B]): Validated[E, B] =
209+
def ap[A,B](fa: Validated[E,A])(f: Validated[E,A=>B]): Validated[E, B] =
200210
fa.ap(f)(E)
211+
212+
def product[A, B](fa: Validated[E, A], fb: Validated[E, B]): Validated[E, (A, B)] =
213+
fa.product(fb)(E)
201214
}
202215
}
203216

core/src/main/scala/cats/syntax/all.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package syntax
33

44
trait AllSyntax
55
extends ApplySyntax
6+
with MonoidalSyntax
67
with BifunctorSyntax
78
with CoflatMapSyntax
89
with ComonadSyntax

0 commit comments

Comments
 (0)