Skip to content

Commit 977d41e

Browse files
SystemFwkailuowang
authored andcommitted
standardise on liftF and add liftK to transformers (#2033)
* Deprecate `lift` in favour of `liftF` * Add `liftK` to Monad Transformers * Correct version number in deprecation warning * Add scalafix to rename `lift` to `liftF` * Use kind projector syntax for liftK * Add MiMa exceptions * Fix scalafix * Fix broken scaladoc link * Fix doctest for Kleisli's liftK
1 parent 6f67547 commit 977d41e

File tree

15 files changed

+162
-16
lines changed

15 files changed

+162
-16
lines changed

build.sbt

+6-1
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,16 @@ def mimaSettings(moduleName: String) = Seq(
242242
exclude[ReversedMissingMethodProblem]("cats.instances.ParallelInstances.catsStdNonEmptyParallelForZipList"),
243243
exclude[ReversedMissingMethodProblem]("cats.instances.ParallelInstances.catsStdParallelForFailFastFuture"),
244244
exclude[DirectMissingMethodProblem]("cats.data.EitherTInstances2.catsDataMonadErrorForEitherT"),
245-
exclude[DirectMissingMethodProblem]("cats.data.EitherTInstances2.catsDataMonadErrorForEitherT"),
246245
exclude[ReversedMissingMethodProblem]("cats.Foldable.collectFirstSome"),
247246
exclude[ReversedMissingMethodProblem]("cats.Foldable.collectFirst"),
248247
exclude[ReversedMissingMethodProblem]("cats.Foldable#Ops.collectFirstSome"),
249248
exclude[ReversedMissingMethodProblem]("cats.Foldable#Ops.collectFirst"),
249+
exclude[ReversedMissingMethodProblem]("cats.data.CommonIRWSTConstructors.liftF"),
250+
exclude[ReversedMissingMethodProblem]("cats.data.CommonIRWSTConstructors.liftK"),
251+
exclude[ReversedMissingMethodProblem]("cats.data.KleisliFunctions.liftF"),
252+
exclude[ReversedMissingMethodProblem]("cats.data.KleisliFunctions.liftK"),
253+
exclude[ReversedMissingMethodProblem]("cats.data.CommonStateTConstructors.liftF"),
254+
exclude[ReversedMissingMethodProblem]("cats.data.CommonStateTConstructors.liftK"),
250255
exclude[ReversedMissingMethodProblem]("cats.NonEmptyParallel.parForEffect"),
251256
exclude[ReversedMissingMethodProblem]("cats.NonEmptyParallel.parFollowedBy"),
252257
exclude[ReversedMissingMethodProblem]("cats.syntax.ParallelSyntax.catsSyntaxParallelAp"),

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

+13
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,19 @@ object EitherT extends EitherTInstances {
352352
*/
353353
final def liftF[F[_], A, B](fb: F[B])(implicit F: Functor[F]): EitherT[F, A, B] = right(fb)
354354

355+
/**
356+
* Same as [[liftF]], but expressed as a FunctionK for use with mapK
357+
* {{{
358+
* scala> import cats._, data._, implicits._
359+
* scala> val a: OptionT[Eval, Int] = 1.pure[OptionT[Eval, ?]]
360+
* scala> val b: OptionT[EitherT[Eval, String, ?], Int] = a.mapK(EitherT.liftK)
361+
* scala> b.value.value.value
362+
* res0: Either[String,Option[Int]] = Right(Some(1))
363+
* }}}
364+
*/
365+
final def liftK[F[_], A](implicit F: Functor[F]): F ~> EitherT[F, A, ?] =
366+
λ[F ~> EitherT[F, A, ?]](right(_))
367+
355368
@deprecated("Use EitherT.liftF.", "1.0.0-RC1")
356369
final def liftT[F[_], A, B](fb: F[B])(implicit F: Functor[F]): EitherT[F, A, B] = right(fb)
357370

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

+19-2
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,23 @@ private[data] sealed trait CommonIRWSTConstructors {
266266
/**
267267
* Return an effectful `a` and an empty log without modifying the input state.
268268
*/
269+
def liftF[F[_], E, L, S, A](fa: F[A])(implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, S, S, A] =
270+
IndexedReaderWriterStateT((_, s) => F.map(fa)((L.empty, s, _)))
271+
272+
/**
273+
* Same as [[liftF]], but expressed as a FunctionK for use with mapK
274+
* {{{
275+
* scala> import cats._, data._, implicits._
276+
* scala> val a: OptionT[Eval, Int] = 1.pure[OptionT[Eval, ?]]
277+
* scala> val b: OptionT[RWST[Eval, Boolean, List[String], String, ?], Int] = a.mapK(RWST.liftK)
278+
* scala> b.value.runEmpty(true).value
279+
* res0: (List[String], String, Option[Int]) = (List(),"",Some(1))
280+
* }}}
281+
*/
282+
def liftK[F[_], E, L, S](implicit F: Applicative[F], L: Monoid[L]): F ~> IndexedReaderWriterStateT[F, E, L, S, S, ?] =
283+
λ[F ~> IndexedReaderWriterStateT[F, E, L, S, S, ?]](IndexedReaderWriterStateT.liftF(_))
284+
285+
@deprecated("Use liftF instead", "1.0.0")
269286
def lift[F[_], E, L, S, A](fa: F[A])(implicit F: Applicative[F], L: Monoid[L]): IndexedReaderWriterStateT[F, E, L, S, S, A] =
270287
IndexedReaderWriterStateT((_, s) => F.map(fa)((L.empty, s, _)))
271288

@@ -570,7 +587,7 @@ private[data] sealed abstract class RWSTAlternative[F[_], E, L, S]
570587
G.combineK(x.run(e, sa), y.run(e, sa))
571588
}
572589

573-
def empty[A]: ReaderWriterStateT[F, E, L, S, A] = ReaderWriterStateT.lift(G.empty[A])
590+
def empty[A]: ReaderWriterStateT[F, E, L, S, A] = ReaderWriterStateT.liftF(G.empty[A])
574591

575592
def pure[A](a: A): ReaderWriterStateT[F, E, L, S, A] = ReaderWriterStateT.pure[F, E, L, S, A](a)
576593

@@ -584,7 +601,7 @@ private[data] sealed abstract class RWSTMonadError[F[_], E, L, S, R]
584601

585602
implicit def F: MonadError[F, R]
586603

587-
def raiseError[A](r: R): ReaderWriterStateT[F, E, L, S, A] = ReaderWriterStateT.lift(F.raiseError(r))
604+
def raiseError[A](r: R): ReaderWriterStateT[F, E, L, S, A] = ReaderWriterStateT.liftF(F.raiseError(r))
588605

589606
def handleErrorWith[A](fa: ReaderWriterStateT[F, E, L, S, A])(f: R => ReaderWriterStateT[F, E, L, S, A]): ReaderWriterStateT[F, E, L, S, A] =
590607
ReaderWriterStateT { (e, s) =>

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

+19-2
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,23 @@ private[data] trait CommonStateTConstructors {
173173
def pure[F[_], S, A](a: A)(implicit F: Applicative[F]): IndexedStateT[F, S, S, A] =
174174
IndexedStateT(s => F.pure((s, a)))
175175

176+
def liftF[F[_], S, A](fa: F[A])(implicit F: Applicative[F]): IndexedStateT[F, S, S, A] =
177+
IndexedStateT(s => F.map(fa)(a => (s, a)))
178+
179+
/**
180+
* Same as [[liftF]], but expressed as a FunctionK for use with mapK
181+
* {{{
182+
* scala> import cats._, data._, implicits._
183+
* scala> val a: OptionT[Eval, Int] = 1.pure[OptionT[Eval, ?]]
184+
* scala> val b: OptionT[StateT[Eval, String, ?], Int] = a.mapK(StateT.liftK)
185+
* scala> b.value.runEmpty.value
186+
* res0: (String, Option[Int]) = ("",Some(1))
187+
* }}}
188+
*/
189+
def liftK[F[_], S](implicit F: Applicative[F]): F ~> IndexedStateT[F, S, S, ?] =
190+
λ[F ~> IndexedStateT[F, S, S, ?]](IndexedStateT.liftF(_))
191+
192+
@deprecated("Use liftF instead", "1.0.0")
176193
def lift[F[_], S, A](fa: F[A])(implicit F: Applicative[F]): IndexedStateT[F, S, S, A] =
177194
IndexedStateT(s => F.map(fa)(a => (s, a)))
178195

@@ -369,14 +386,14 @@ private[data] sealed abstract class IndexedStateTAlternative[F[_], S] extends In
369386
IndexedStateT[F, S, S, A](s => G.combineK(x.run(s), y.run(s)))(G)
370387

371388
def empty[A]: IndexedStateT[F, S, S, A] =
372-
IndexedStateT.lift[F, S, A](G.empty[A])(G)
389+
IndexedStateT.liftF[F, S, A](G.empty[A])(G)
373390
}
374391

375392
private[data] sealed abstract class IndexedStateTMonadError[F[_], S, E] extends IndexedStateTMonad[F, S]
376393
with MonadError[IndexedStateT[F, S, S, ?], E] {
377394
implicit def F: MonadError[F, E]
378395

379-
def raiseError[A](e: E): IndexedStateT[F, S, S, A] = IndexedStateT.lift(F.raiseError(e))
396+
def raiseError[A](e: E): IndexedStateT[F, S, S, A] = IndexedStateT.liftF(F.raiseError(e))
380397

381398
def handleErrorWith[A](fa: IndexedStateT[F, S, S, A])(f: E => IndexedStateT[F, S, S, A]): IndexedStateT[F, S, S, A] =
382399
IndexedStateT(s => F.handleErrorWith(fa.run(s))(e => f(e).run(s)))

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

+18-1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,23 @@ object Kleisli extends KleisliInstances with KleisliFunctions with KleisliExplic
8484

8585
private[data] sealed trait KleisliFunctions {
8686

87+
def liftF[F[_], A, B](x: F[B]): Kleisli[F, A, B] =
88+
Kleisli(_ => x)
89+
90+
/**
91+
* Same as [[liftF]], but expressed as a FunctionK for use with mapK
92+
* {{{
93+
* scala> import cats._, data._, implicits._
94+
* scala> val a: OptionT[Eval, Int] = 1.pure[OptionT[Eval, ?]]
95+
* scala> val b: OptionT[Kleisli[Eval, String, ?], Int] = a.mapK(Kleisli.liftK)
96+
* scala> b.value.run("").value
97+
* res0: Option[Int] = Some(1)
98+
* }}}
99+
*/
100+
def liftK[F[_], A]: F ~> Kleisli[F, A, ?] =
101+
λ[F ~> Kleisli[F, A, ?]](Kleisli.liftF(_))
102+
103+
@deprecated("Use liftF instead", "1.0.0")
87104
def lift[F[_], A, B](x: F[B]): Kleisli[F, A, B] =
88105
Kleisli(_ => x)
89106

@@ -287,7 +304,7 @@ private[data] sealed trait KleisliSemigroupK[F[_], A] extends SemigroupK[Kleisli
287304
private[data] sealed trait KleisliMonoidK[F[_], A] extends MonoidK[Kleisli[F, A, ?]] with KleisliSemigroupK[F, A] {
288305
implicit def F: MonoidK[F]
289306

290-
override def empty[B]: Kleisli[F, A, B] = Kleisli.lift(F.empty[B])
307+
override def empty[B]: Kleisli[F, A, B] = Kleisli.liftF(F.empty[B])
291308
}
292309

293310
private[data] trait KleisliAlternative[F[_], A] extends Alternative[Kleisli[F, A, ?]] with KleisliApplicative[F, A] with KleisliMonoidK[F, A] {

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

+13
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,19 @@ object OptionT extends OptionTInstances {
198198
* Lifts the `F[A]` Functor into an `OptionT[F, A]`.
199199
*/
200200
def liftF[F[_], A](fa: F[A])(implicit F: Functor[F]): OptionT[F, A] = OptionT(F.map(fa)(Some(_)))
201+
202+
/**
203+
* Same as [[liftF]], but expressed as a FunctionK for use with mapK
204+
* {{{
205+
* scala> import cats._, data._, implicits._
206+
* scala> val a: EitherT[Eval, String, Int] = 1.pure[EitherT[Eval, String, ?]]
207+
* scala> val b: EitherT[OptionT[Eval, ?], String, Int] = a.mapK(OptionT.liftK)
208+
* scala> b.value.value.value
209+
* res0: Option[Either[String,Int]] = Some(Right(1))
210+
* }}}
211+
*/
212+
def liftK[F[_]](implicit F: Functor[F]): F ~> OptionT[F, ?] =
213+
λ[F ~> OptionT[F, ?]](OptionT.liftF(_))
201214
}
202215

203216
private[data] sealed abstract class OptionTInstances extends OptionTInstances0 {

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

+17
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,23 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) {
6767

6868
object WriterT extends WriterTInstances with WriterTFunctions {
6969

70+
def liftF[F[_], L, V](fv: F[V])(implicit monoidL: Monoid[L], F: Applicative[F]): WriterT[F, L, V] =
71+
WriterT(F.map(fv)(v => (monoidL.empty, v)))
72+
73+
/**
74+
* Same as [[liftF]], but expressed as a FunctionK for use with mapK
75+
* {{{
76+
* scala> import cats._, data._, implicits._
77+
* scala> val a: OptionT[Eval, Int] = 1.pure[OptionT[Eval, ?]]
78+
* scala> val b: OptionT[WriterT[Eval, String, ?], Int] = a.mapK(WriterT.liftK)
79+
* scala> b.value.run.value
80+
* res0: (String, Option[Int]) = ("",Some(1))
81+
* }}}
82+
*/
83+
def liftK[F[_], L](implicit monoidL: Monoid[L], F: Applicative[F]): F ~> WriterT[F, L, ?] =
84+
λ[F ~> WriterT[F, L, ?]](WriterT.liftF(_))
85+
86+
@deprecated("Use liftF instead", "1.0.0")
7087
def lift[F[_], L, V](fv: F[V])(implicit monoidL: Monoid[L], F: Applicative[F]): WriterT[F, L, V] =
7188
WriterT(F.map(fv)(v => (monoidL.empty, v)))
7289

scalafix/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ sbt scalafix github:typelevel/cats/v1.0.0
2424

2525
- [x] EitherT.liftT was renamed to EitherT.liftF
2626

27+
- [x] the lift method on WriterT, StateT, RWST and Kleisli was renamed to liftF
28+
2729
- [x] CartesianBuilder (i.e. |@|) syntax is deprecated, use the apply syntax on tuples instead. E.g. (x |@| y |@| z).map(...) should be replaced by (x, y, z).mapN(...)
2830

2931
- [x] Free.suspend is renamed to Free.defer for consistency.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
rule = "scala:fix.v1_0_0.RenameTransformersLift"
3+
*/
4+
package fix
5+
package to1_0_0
6+
7+
object RenameTransformersLiftTests {
8+
import cats.instances.option._
9+
import cats.instances.string._
10+
import cats.data.{Kleisli, StateT, WriterT}
11+
12+
val fa: Option[Int] = Some(42)
13+
val k: Kleisli[Option, Nothing, Int] = Kleisli.lift(fa)
14+
val w: WriterT[Option, String, Int] = WriterT.lift(fa)
15+
val s: StateT[Option, Nothing, Int] = StateT.lift(fa)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package fix
2+
package to1_0_0
3+
4+
object RenameTransformersLiftTests {
5+
import cats.instances.option._
6+
import cats.instances.string._
7+
import cats.data.{Kleisli, StateT, WriterT}
8+
9+
val fa: Option[Int] = Some(42)
10+
val k: Kleisli[Option, Nothing, Int] = Kleisli.liftF(fa)
11+
val w: WriterT[Option, String, Int] = WriterT.liftF(fa)
12+
val s: StateT[Option, Nothing, Int] = StateT.liftF(fa)
13+
}

scalafix/rules/src/main/scala/fix/Cats_v1_0_0.scala

+16
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,22 @@ case class RenameEitherTLiftT(index: SemanticdbIndex)
227227

228228
}
229229

230+
// ref: https://github.com/typelevel/cats/pull/2033
231+
case class RenameTransformersLift(index: SemanticdbIndex)
232+
extends SemanticRule(index, "RenameTransformersLift") {
233+
234+
override def fix(ctx: RuleCtx): Patch =
235+
ctx.replaceSymbols(
236+
"_root_.cats.data.WriterT.lift." -> "liftF",
237+
"_root_.cats.data.StateT.lift." -> "liftF",
238+
"_root_.cats.data.CommonStateTConstructors.lift." -> "liftF",
239+
"_root_.cats.data.CommonIRWSTConstructors.lift." -> "liftF",
240+
"_root_.cats.data.KleisliFunctions.lift." -> "liftF"
241+
)
242+
243+
}
244+
245+
230246
// ref: https://github.com/typelevel/cats/pull/1961
231247
case class RenameCartesian(index: SemanticdbIndex)
232248
extends SemanticRule(index, "RenameCartesian") {

tests/src/test/scala/cats/tests/IndexedReaderWriterStateTSuite.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,11 @@ class ReaderWriterStateTSuite extends CatsSuite {
138138
}
139139
}
140140

141-
test("ReaderWriterState.pure, ReaderWriterStateT.lift and IndexedReaderWriterStateT.lift are consistent") {
141+
test("ReaderWriterState.pure, ReaderWriterStateT.liftF and IndexedReaderWriterStateT.liftF are consistent") {
142142
forAll { (value: Int) =>
143143
val rws: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterState.pure(value)
144-
val rwst: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterStateT.lift(Eval.now(value))
145-
val irwst: ReaderWriterState[String, Vector[String], Int, Int] = IndexedReaderWriterStateT.lift(Eval.now(value))
144+
val rwst: ReaderWriterState[String, Vector[String], Int, Int] = ReaderWriterStateT.liftF(Eval.now(value))
145+
val irwst: ReaderWriterState[String, Vector[String], Int, Int] = IndexedReaderWriterStateT.liftF(Eval.now(value))
146146

147147
rws should === (rwst)
148148
rwst should === (irwst)

tests/src/test/scala/cats/tests/IndexedStateTSuite.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,11 @@ class IndexedStateTSuite extends CatsSuite {
110110
}
111111
}
112112

113-
test("State.pure, StateT.lift and IndexedStateT.lift are consistent") {
113+
test("State.pure, StateT.liftF and IndexedStateT.liftF are consistent") {
114114
forAll { (s: String, i: Int) =>
115115
val state: State[String, Int] = State.pure(i)
116-
val stateT: State[String, Int] = StateT.lift(Eval.now(i))
117-
val indexedStateT: State[String, Int] = IndexedStateT.lift(Eval.now(i))
116+
val stateT: State[String, Int] = StateT.liftF(Eval.now(i))
117+
val indexedStateT: State[String, Int] = IndexedStateT.liftF(Eval.now(i))
118118

119119
state.run(s) should === (stateT.run(s))
120120
state.run(s) should === (indexedStateT.run(s))

tests/src/test/scala/cats/tests/ParallelSuite.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ class ParallelSuite extends CatsSuite with ApplicativeErrorForEitherTest {
100100
}
101101

102102
test("WriterT with Either should accumulate errors") {
103-
val w1: WriterT[Either[String, ?], String, Int] = WriterT.lift(Left("Too "))
104-
val w2: WriterT[Either[String, ?], String, Int] = WriterT.lift(Left("bad."))
103+
val w1: WriterT[Either[String, ?], String, Int] = WriterT.liftF(Left("Too "))
104+
val w2: WriterT[Either[String, ?], String, Int] = WriterT.liftF(Left("bad."))
105105

106106
((w1,w2).parMapN(_ + _).value) should === (Left("Too bad."))
107107

tests/src/test/scala/cats/tests/WriterTSuite.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ class WriterTSuite extends CatsSuite {
6565
}
6666
}
6767

68-
test("Writer.pure and WriterT.lift are consistent") {
68+
test("Writer.pure and WriterT.liftF are consistent") {
6969
forAll { (i: Int) =>
7070
val writer: Writer[String, Int] = Writer.value(i)
71-
val writerT: WriterT[Option, String, Int] = WriterT.lift(Some(i))
71+
val writerT: WriterT[Option, String, Int] = WriterT.liftF(Some(i))
7272
writer.run.some should === (writerT.run)
7373
}
7474
}

0 commit comments

Comments
 (0)