From 7f45e3b4658662ca4afb8dc9594df299754aef67 Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Wed, 12 Apr 2023 20:38:54 -0300 Subject: [PATCH 1/5] Wip: doctests for FlatMap and Monad --- core/src/main/scala/cats/FlatMap.scala | 25 +++++++++++++++++++++++++ core/src/main/scala/cats/Monad.scala | 22 ++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/core/src/main/scala/cats/FlatMap.scala b/core/src/main/scala/cats/FlatMap.scala index 1adeb60fa3..1a15ca33a6 100644 --- a/core/src/main/scala/cats/FlatMap.scala +++ b/core/src/main/scala/cats/FlatMap.scala @@ -137,6 +137,22 @@ trait FlatMap[F[_]] extends Apply[F] with FlatMapArityFunctions[F] { /** * `if` lifted into monad. + * + * Example: + * {{{ + * scala> import cats.Eval + * scala> import cats.syntax.all._ + * + * scala> val b1 = Eval.now(true) + * scala> val asBool1 = b1.ifM(Eval.now(true), Eval.now(false)) + * scala> asBool1.value + * res0: Boolean = true + + * scala> val b2 = Eval.now(false) + * scala> val asBool2 = b2.ifM(Eval.now(true), Eval.now(false)) + * scala> asBool2.value + * res1: Boolean = false + * }}} */ def ifM[B](fa: F[Boolean])(ifTrue: => F[B], ifFalse: => F[B]): F[B] = @@ -207,6 +223,15 @@ trait FlatMap[F[_]] extends Apply[F] with FlatMapArityFunctions[F] { * This repeats an F until we get defined values. This can be useful * for polling type operations on State (or RNG) Monads, or in effect * monads. + * + * Example: + * {{{ + * scala> import cats.data.State + * scala> val counter = State { i: Int => (i+1, if(i>100) Some(i) else None)} + * scala> val eval = counter.untilDefinedM.run(0) + * scala> eval.value + * res0: (Int, Int) = (102,101) + * }}} */ def untilDefinedM[A](foa: F[Option[A]]): F[A] = { diff --git a/core/src/main/scala/cats/Monad.scala b/core/src/main/scala/cats/Monad.scala index c212fc7038..2fbe855d13 100644 --- a/core/src/main/scala/cats/Monad.scala +++ b/core/src/main/scala/cats/Monad.scala @@ -40,6 +40,18 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * Collects the results into an arbitrary `Alternative` value, such as a `Vector`. * This implementation uses append on each evaluation result, * so avoid data structures with non-constant append performance, e.g. `List`. + * + * Example: + * {{{ + * scala> import cats.{Id, Monad} + * scala> import cats.data.StateT + * scala> import cats.syntax.all._ + * scala> val appendStr: StateT[Id, String, Unit] = StateT.modify(_ + "a") + * scala> val appendStrAndGet: StateT[Id, String, String] = appendStr *> StateT.get + * scala> val (result, agg) = appendStrAndGet.whileM[Vector](StateT.inspect(i => !(i.length >= 5))).run("") + * result: String = aaaaa + * agg: Vector[String] = Vector(a, aa, aaa, aaaa, aaaaa) + * }}} */ def whileM[G[_], A](p: F[Boolean])(body: => F[A])(implicit G: Alternative[G]): F[G[A]] = { @@ -60,6 +72,16 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * Execute an action repeatedly as long as the given `Boolean` expression * returns `true`. The condition is evaluated before the loop body. * Discards results. + * + * Example: + * {{{ + * scala> import cats.{Id, Monad} + * scala> import cats.data.StateT + * scala> import cats.syntax.all._ + * scala> val appendStr: StateT[Id, String, Unit] = StateT.modify(_ + "a") + * scala> val (result, _) = appendStr.whileM_(StateT.inspect(i => !(i.length >= 3))).run("") + * result: String = aaa + * }}} */ def whileM_[A](p: F[Boolean])(body: => F[A]): F[Unit] = { From 7fd2fa2103942a085e7aff75c58917b949673768 Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Mon, 17 Apr 2023 10:32:04 -0300 Subject: [PATCH 2/5] Adding more doctests to Monad --- core/src/main/scala/cats/Monad.scala | 68 ++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/core/src/main/scala/cats/Monad.scala b/core/src/main/scala/cats/Monad.scala index 2fbe855d13..648b45fda6 100644 --- a/core/src/main/scala/cats/Monad.scala +++ b/core/src/main/scala/cats/Monad.scala @@ -102,6 +102,18 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * arbitrary `Alternative` value, such as a `Vector`. * This implementation uses append on each evaluation result, * so avoid data structures with non-constant append performance, e.g. `List`. + * + * Example: + * {{{ + * scala> import cats.{Id, Monad} + * scala> import cats.data.StateT + * scala> import cats.syntax.all._ + * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) + * scala> val incrementAndGet: StateT[Id, Int, Int] = increment *> StateT.get + * scala> val (result, aggregation) = incrementAndGet.untilM[Vector](StateT.inspect(_ >= 5)).run(-1) + * result: Int = 5 + * aggregation: Vector[Int] = Vector(0, 1, 2, 3, 4, 5) + * }}} */ def untilM[G[_], A](f: F[A])(cond: => F[Boolean])(implicit G: Alternative[G]): F[G[A]] = { val p = Eval.later(cond) @@ -111,6 +123,16 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { /** * Execute an action repeatedly until the `Boolean` condition returns `true`. * The condition is evaluated after the loop body. Discards results. + * + * Example: + * {{{ + * scala> import cats.{Id, Monad} + * scala> import cats.data.StateT + * scala> import cats.syntax.all._ + * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) + * scala> val (result, _) = increment.untilM_(StateT.inspect(_ >= 5)).run(-1) + * result: Int = 5 + * }}} */ def untilM_[A](f: F[A])(cond: => F[Boolean]): F[Unit] = { val p = Eval.later(cond) @@ -120,6 +142,17 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { /** * Execute an action repeatedly until its result fails to satisfy the given predicate * and return that result, discarding all others. + * + * Example: + * {{{ + * scala> import cats.{Id, Monad} + * scala> import cats.data.StateT + * scala> import cats.syntax.all._ + * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) + * scala> val incrementAndGet: StateT[Id, Int, Int] = increment *> StateT.get + * scala> val (result, _) = incrementAndGet.iterateWhile(_ < 5).run(-1) + * result: Int = 5 + * }}} */ def iterateWhile[A](f: F[A])(p: A => Boolean): F[A] = flatMap(f) { i => @@ -129,6 +162,17 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { /** * Execute an action repeatedly until its result satisfies the given predicate * and return that result, discarding all others. + * + * Example: + * {{{ + * scala> import cats.{Id, Monad} + * scala> import cats.data.StateT + * scala> import cats.syntax.all._ + * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) + * scala> val incrementAndGet: StateT[Id, Int, Int] = increment *> StateT.get + * scala> val (result, _) = incrementAndGet.iterateUntil(_ == 5).run(-1) + * result: Int = 5 + * }}} */ def iterateUntil[A](f: F[A])(p: A => Boolean): F[A] = flatMap(f) { i => @@ -138,6 +182,18 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { /** * Apply a monadic function iteratively until its result fails * to satisfy the given predicate and return that result. + * + * Example: + * {{{ + * scala> import cats.{Id, Monad} + * scala> import cats.data.StateT + * scala> import cats.syntax.all._ + * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) + * scala> val incrementAndGet: StateT[Id, Int, Int] = increment *> StateT.get + * scala> val (n, sum) = 0.iterateWhileM(s => incrementAndGet.map(_ + s))(_ < 5).run(0) + * n: Int = 3 + * sum: Int = 6 + * }}} */ def iterateWhileM[A](init: A)(f: A => F[A])(p: A => Boolean): F[A] = tailRecM(init) { a => @@ -150,6 +206,18 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { /** * Apply a monadic function iteratively until its result satisfies * the given predicate and return that result. + * + * Example: + * {{{ + * scala> import cats.{Id, Monad} + * scala> import cats.data.StateT + * scala> import cats.syntax.all._ + * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) + * scala> val incrementAndGet: StateT[Id, Int, Int] = increment *> StateT.get + * scala> val (n, sum) = 0.iterateUntilM(s => incrementAndGet.map(_ + s))(_ > 5).run(0) + * n: Int = 3 + * sum: Int = 6 + * }}} */ def iterateUntilM[A](init: A)(f: A => F[A])(p: A => Boolean): F[A] = iterateWhileM(init)(f)(!p(_)) From 2510abe889c44d2825ddd3f7de93c71ed86d9968 Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Mon, 17 Apr 2023 10:42:27 -0300 Subject: [PATCH 3/5] fix: FlatMap counter example --- core/src/main/scala/cats/FlatMap.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/scala/cats/FlatMap.scala b/core/src/main/scala/cats/FlatMap.scala index 1a15ca33a6..e58825e799 100644 --- a/core/src/main/scala/cats/FlatMap.scala +++ b/core/src/main/scala/cats/FlatMap.scala @@ -227,6 +227,7 @@ trait FlatMap[F[_]] extends Apply[F] with FlatMapArityFunctions[F] { * Example: * {{{ * scala> import cats.data.State + * scala> import cats.syntax.all._ * scala> val counter = State { i: Int => (i+1, if(i>100) Some(i) else None)} * scala> val eval = counter.untilDefinedM.run(0) * scala> eval.value From ecac102c404a36dabd5f16c1e6b65653dd58052b Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Sat, 20 May 2023 13:31:38 -0300 Subject: [PATCH 4/5] improve ifM doctests, add string instead of boolean --- core/src/main/scala/cats/FlatMap.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/cats/FlatMap.scala b/core/src/main/scala/cats/FlatMap.scala index e58825e799..7f79a9b0e7 100644 --- a/core/src/main/scala/cats/FlatMap.scala +++ b/core/src/main/scala/cats/FlatMap.scala @@ -144,14 +144,14 @@ trait FlatMap[F[_]] extends Apply[F] with FlatMapArityFunctions[F] { * scala> import cats.syntax.all._ * * scala> val b1 = Eval.now(true) - * scala> val asBool1 = b1.ifM(Eval.now(true), Eval.now(false)) - * scala> asBool1.value - * res0: Boolean = true + * scala> val asString1 = b1.ifM(Eval.now("it's true!"), Eval.now("it's false!")) + * scala> asString1.value + * res0: String = it's true! * scala> val b2 = Eval.now(false) - * scala> val asBool2 = b2.ifM(Eval.now(true), Eval.now(false)) - * scala> asBool2.value - * res1: Boolean = false + * scala> val asString2 = b2.ifM(Eval.now("it's true!"), Eval.now("it's false!")) + * scala> asString2.value + * res1: String = it's false! * }}} */ From 4d72a8b5a8aa6519c869f3bc8a3fccef64b52407 Mon Sep 17 00:00:00 2001 From: Diogo Canut Date: Tue, 22 Aug 2023 20:58:43 -0300 Subject: [PATCH 5/5] Using State instead of StateT --- core/src/main/scala/cats/Monad.scala | 76 ++++++++++++---------------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/core/src/main/scala/cats/Monad.scala b/core/src/main/scala/cats/Monad.scala index 648b45fda6..cf30db044a 100644 --- a/core/src/main/scala/cats/Monad.scala +++ b/core/src/main/scala/cats/Monad.scala @@ -43,14 +43,12 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * * Example: * {{{ - * scala> import cats.{Id, Monad} - * scala> import cats.data.StateT + * scala> import cats.data.State * scala> import cats.syntax.all._ - * scala> val appendStr: StateT[Id, String, Unit] = StateT.modify(_ + "a") - * scala> val appendStrAndGet: StateT[Id, String, String] = appendStr *> StateT.get - * scala> val (result, agg) = appendStrAndGet.whileM[Vector](StateT.inspect(i => !(i.length >= 5))).run("") + * scala> val appendStr = State { i: String => (i+"a", i)} + * scala> val (result, agg) = appendStr.whileM[Vector](State.inspect(i => !(i.length >= 5))).run("").value * result: String = aaaaa - * agg: Vector[String] = Vector(a, aa, aaa, aaaa, aaaaa) + * agg: Vector[String] = Vector("", a, aa, aaa, aaaa) * }}} */ @@ -75,11 +73,10 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * * Example: * {{{ - * scala> import cats.{Id, Monad} - * scala> import cats.data.StateT + * scala> import cats.data.State * scala> import cats.syntax.all._ - * scala> val appendStr: StateT[Id, String, Unit] = StateT.modify(_ + "a") - * scala> val (result, _) = appendStr.whileM_(StateT.inspect(i => !(i.length >= 3))).run("") + * scala> val appendStr = State { i: String => (i+"a", i)} + * scala> val (result, _) = appendStr.whileM_(State.inspect(i => !(i.length >= 3))).run("").value * result: String = aaa * }}} */ @@ -105,14 +102,12 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * * Example: * {{{ - * scala> import cats.{Id, Monad} - * scala> import cats.data.StateT + * scala> import cats.data.State * scala> import cats.syntax.all._ - * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) - * scala> val incrementAndGet: StateT[Id, Int, Int] = increment *> StateT.get - * scala> val (result, aggregation) = incrementAndGet.untilM[Vector](StateT.inspect(_ >= 5)).run(-1) + * scala> val increment = State { i: Int => (i+1, i)} + * scala> val (result, aggregation) = increment.untilM[Vector](State.inspect(_ >= 5)).run(-1).value * result: Int = 5 - * aggregation: Vector[Int] = Vector(0, 1, 2, 3, 4, 5) + * aggregation: Vector[Int] = Vector(-1, 0, 1, 2, 3, 4) * }}} */ def untilM[G[_], A](f: F[A])(cond: => F[Boolean])(implicit G: Alternative[G]): F[G[A]] = { @@ -126,11 +121,10 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * * Example: * {{{ - * scala> import cats.{Id, Monad} - * scala> import cats.data.StateT + * scala> import cats.data.State * scala> import cats.syntax.all._ - * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) - * scala> val (result, _) = increment.untilM_(StateT.inspect(_ >= 5)).run(-1) + * scala> val increment = State { i: Int => (i+1, i)} + * scala> val (result, _) = increment.untilM_(State.inspect(_ >= 5)).run(-1).value * result: Int = 5 * }}} */ @@ -145,13 +139,12 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * * Example: * {{{ - * scala> import cats.{Id, Monad} - * scala> import cats.data.StateT + * scala> import cats.data.State * scala> import cats.syntax.all._ - * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) - * scala> val incrementAndGet: StateT[Id, Int, Int] = increment *> StateT.get - * scala> val (result, _) = incrementAndGet.iterateWhile(_ < 5).run(-1) - * result: Int = 5 + * scala> val increment = State { i: Int => (i+1, i)} + * scala> val (result, agg) = increment.iterateWhile(_ < 5).run(-1).value + * result: Int = 6 + * agg: Int = 5 * }}} */ def iterateWhile[A](f: F[A])(p: A => Boolean): F[A] = @@ -165,13 +158,12 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * * Example: * {{{ - * scala> import cats.{Id, Monad} - * scala> import cats.data.StateT + * scala> import cats.data.State * scala> import cats.syntax.all._ - * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) - * scala> val incrementAndGet: StateT[Id, Int, Int] = increment *> StateT.get - * scala> val (result, _) = incrementAndGet.iterateUntil(_ == 5).run(-1) - * result: Int = 5 + * scala> val increment = State { i: Int => (i+1, i)} + * scala> val (result, agg) = increment.iterateUntil(_ == 5).run(-1).value + * result: Int = 6 + * agg: Int = 5 * }}} */ def iterateUntil[A](f: F[A])(p: A => Boolean): F[A] = @@ -185,13 +177,11 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * * Example: * {{{ - * scala> import cats.{Id, Monad} - * scala> import cats.data.StateT + * scala> import cats.data.State * scala> import cats.syntax.all._ - * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) - * scala> val incrementAndGet: StateT[Id, Int, Int] = increment *> StateT.get - * scala> val (n, sum) = 0.iterateWhileM(s => incrementAndGet.map(_ + s))(_ < 5).run(0) - * n: Int = 3 + * scala> val increment = State { i: Int => (i+1, i)} + * scala> val (n, sum) = 0.iterateWhileM(s => increment.map(_ + s))(_ < 5).run(0).value + * n: Int = 4 * sum: Int = 6 * }}} */ @@ -209,13 +199,11 @@ trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { * * Example: * {{{ - * scala> import cats.{Id, Monad} - * scala> import cats.data.StateT + * scala> import cats.data.State * scala> import cats.syntax.all._ - * scala> val increment: StateT[Id, Int, Unit] = StateT.modify(_ + 1) - * scala> val incrementAndGet: StateT[Id, Int, Int] = increment *> StateT.get - * scala> val (n, sum) = 0.iterateUntilM(s => incrementAndGet.map(_ + s))(_ > 5).run(0) - * n: Int = 3 + * scala> val increment = State { i: Int => (i+1, i)} + * scala> val (n, sum) = 0.iterateUntilM(s => increment.map(_ + s))(_ > 5).run(0).value + * n: Int = 4 * sum: Int = 6 * }}} */