From 37a7f9b670a5955b483e030e9977cc06bc2f4c69 Mon Sep 17 00:00:00 2001 From: Kevin Won Date: Thu, 24 Aug 2023 15:58:01 -0700 Subject: [PATCH 1/9] adds more details to random.md documentation --- docs/std/random.md | 71 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/docs/std/random.md b/docs/std/random.md index f00ab03be4..9f4a849ad0 100644 --- a/docs/std/random.md +++ b/docs/std/random.md @@ -36,23 +36,72 @@ on the JVM: - `java.util.Random` - `java.security.SecureRandom` -## Creating a `Random` instance +## Creating and using a `Random` instance -Obtaining an instance of `Random` can be as simple as: +The following example shows the usage of `Random` in a way that is *referentially transparent*, meaning that you can know from the 'outside' what the result will be by knowing the inputs: ```scala mdoc:silent -import cats.effect.IO import cats.effect.std.Random - -Random.scalaUtilRandom[IO] +import cats.implicits.* +import cats.Monad +import cats.effect.unsafe.implicits.global + +// Scala 3 syntax +object BusinessLogic: + + // use the standard implementation of Random backed by java.util.Random() + // (the same implementation as Random.javaUtilRandom(43)) + given IO[Random[IO]] = Random.scalaUtilRandom[IO] + + // other possible implemntations you could choose + val sr = SecureRandom.javaSecuritySecureRandom(3) // backed java.security.SecureRandom() + val jr = Random.javaUtilRandom(new java.util.Random()) // pass in the backing randomizer + + // calling .unsafeRunSync() in business logic is an anti-patten. Doing it here just + // to make the example easy to follow. + def unsafeGetMessage: String = + Magic + .getMagicNumber[IO](mult = 5) // instance of Random passed implicitly + .unsafeRunSync() + +object Magic: + + def getMagicNumber[F[_]: Monad](mult: Int)(using randomizer: F[Random[F]]): F[String] = + for + rand <- randomizer.flatMap(random => random.betweenInt(1, 11)) // 11 is excluded + number = rand * mult + msg = s"the magic number is: $number" + yield msg ``` -## Using `Random` -```scala mdoc -import cats.Functor -import cats.syntax.functor._ +Since `getMagicNumber` is not dependent on a particular implementation (it's referentially transparent), you can give it another instance of the type class as you see fit. + +This is particularly useful when testing. In the following example, we need our `Random` implementation give back a stable value so we can ensure everything else works correctly, and our test assert succeeds. Since `randomizer` is passed into `getMagicNumber` we can swap it out in our test with a `Random` of which we can make stable. In our test implementation, calls to `betweenInt` will always give back `7`. This stability of "randomness" allows us to test that our function `getMagicNumber` does what we intend: -def dieRoll[F[_]: Functor: Random]: F[Int] = - Random[F].betweenInt(0, 6).map(_ + 1) // `6` is excluded from the range + + +```scala mdoc +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.must.Matchers.* + +class MagicSpec extends AnyFunSuite: + + // for testing, create a Random instance that gives back the same number ever time. + // with a "stable" version of Random, we + given IO[Random[IO]] = IO( + new Random[IO]: + def betweenInt(minInclusive: Int, maxExclusive: Int): IO[Int] = + IO(7) // gives back 7 every call + + // all other methods not implemented since they won't be called in our test + def betweenDouble(minInclusive: Double, maxExclusive: Double): IO[Double] = ??? + def betweenFloat(minInclusive: Float, maxExclusive: Float): IO[Float] = ??? + // ... snip: cutting out other method implementations for brevity + ) + + test("getMagicNumber text matches expectations") { + val result = MagicSpec.getMagicNumber[IO](5) + result.mustBe("the magic number is: 35") + } ``` ## Derivation From c64d55d314b17f58ef7a022b29ca91aa7e127710 Mon Sep 17 00:00:00 2001 From: Kevin Won Date: Thu, 24 Aug 2023 17:33:39 -0700 Subject: [PATCH 2/9] changing to Scala 2 syntax --- docs/std/random.md | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/std/random.md b/docs/std/random.md index 9f4a849ad0..ab2dc161fd 100644 --- a/docs/std/random.md +++ b/docs/std/random.md @@ -46,31 +46,34 @@ import cats.Monad import cats.effect.unsafe.implicits.global // Scala 3 syntax -object BusinessLogic: +object BusinessLogic { // use the standard implementation of Random backed by java.util.Random() // (the same implementation as Random.javaUtilRandom(43)) - given IO[Random[IO]] = Random.scalaUtilRandom[IO] + implicit r: IO[Random[IO]] = Random.scalaUtilRandom[IO] // other possible implemntations you could choose val sr = SecureRandom.javaSecuritySecureRandom(3) // backed java.security.SecureRandom() val jr = Random.javaUtilRandom(new java.util.Random()) // pass in the backing randomizer - // calling .unsafeRunSync() in business logic is an anti-patten. Doing it here just - // to make the example easy to follow. + // calling .unsafeRunSync() in business logic is an anti-patten. + // Doing it here to make the example easy to follow. def unsafeGetMessage: String = Magic .getMagicNumber[IO](mult = 5) // instance of Random passed implicitly .unsafeRunSync() +} -object Magic: - - def getMagicNumber[F[_]: Monad](mult: Int)(using randomizer: F[Random[F]]): F[String] = +object Magic { + def getMagicNumber[F[_] : Monad]( + mult: Int + )(implicit randomizer: F[Random[F]]): F[String] = for rand <- randomizer.flatMap(random => random.betweenInt(1, 11)) // 11 is excluded number = rand * mult msg = s"the magic number is: $number" yield msg +} ``` Since `getMagicNumber` is not dependent on a particular implementation (it's referentially transparent), you can give it another instance of the type class as you see fit. @@ -83,25 +86,27 @@ This is particularly useful when testing. In the following example, we need our import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.must.Matchers.* -class MagicSpec extends AnyFunSuite: +class MagicSpec extends AnyFunSuite { // for testing, create a Random instance that gives back the same number ever time. // with a "stable" version of Random, we - given IO[Random[IO]] = IO( - new Random[IO]: - def betweenInt(minInclusive: Int, maxExclusive: Int): IO[Int] = + implicit r: IO[Random[IO]] = IO( + new Random[IO] { + def betweenInt(minInclusive: Int, maxExclusive: Int): IO[Int] = IO(7) // gives back 7 every call - + // all other methods not implemented since they won't be called in our test def betweenDouble(minInclusive: Double, maxExclusive: Double): IO[Double] = ??? def betweenFloat(minInclusive: Float, maxExclusive: Float): IO[Float] = ??? - // ... snip: cutting out other method implementations for brevity + // ... snip: cutting out other method implementations for brevity + } ) test("getMagicNumber text matches expectations") { val result = MagicSpec.getMagicNumber[IO](5) result.mustBe("the magic number is: 35") } +} ``` ## Derivation From 6a4dcfe86af9f13abbb3ea382dd2f0e8d620eefe Mon Sep 17 00:00:00 2001 From: Kevin Won Date: Thu, 24 Aug 2023 18:28:14 -0700 Subject: [PATCH 3/9] fixed syntax error --- docs/std/random.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/std/random.md b/docs/std/random.md index ab2dc161fd..98d2bcfd39 100644 --- a/docs/std/random.md +++ b/docs/std/random.md @@ -50,7 +50,7 @@ object BusinessLogic { // use the standard implementation of Random backed by java.util.Random() // (the same implementation as Random.javaUtilRandom(43)) - implicit r: IO[Random[IO]] = Random.scalaUtilRandom[IO] + implicit val r: IO[Random[IO]] = Random.scalaUtilRandom[IO] // other possible implemntations you could choose val sr = SecureRandom.javaSecuritySecureRandom(3) // backed java.security.SecureRandom() @@ -90,7 +90,7 @@ class MagicSpec extends AnyFunSuite { // for testing, create a Random instance that gives back the same number ever time. // with a "stable" version of Random, we - implicit r: IO[Random[IO]] = IO( + implicit val r: IO[Random[IO]] = IO( new Random[IO] { def betweenInt(minInclusive: Int, maxExclusive: Int): IO[Int] = IO(7) // gives back 7 every call From 225260b8c1a9cbf15ccf9cd343d5106b3fe9aee1 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sat, 2 Sep 2023 09:43:51 -0700 Subject: [PATCH 4/9] This is Scala 2 yo, we need brackets --- docs/std/random.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/std/random.md b/docs/std/random.md index 98d2bcfd39..730ab16f3a 100644 --- a/docs/std/random.md +++ b/docs/std/random.md @@ -68,11 +68,11 @@ object Magic { def getMagicNumber[F[_] : Monad]( mult: Int )(implicit randomizer: F[Random[F]]): F[String] = - for + for { rand <- randomizer.flatMap(random => random.betweenInt(1, 11)) // 11 is excluded number = rand * mult msg = s"the magic number is: $number" - yield msg + } yield msg } ``` From 8bc2af16a6edb2f79f618deed9f4a725c8dfce3b Mon Sep 17 00:00:00 2001 From: Kevin Won Date: Mon, 4 Sep 2023 12:50:50 -0700 Subject: [PATCH 5/9] fix grammar mistakes --- docs/std/random.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/std/random.md b/docs/std/random.md index 730ab16f3a..eeee7d00a3 100644 --- a/docs/std/random.md +++ b/docs/std/random.md @@ -45,7 +45,6 @@ import cats.implicits.* import cats.Monad import cats.effect.unsafe.implicits.global -// Scala 3 syntax object BusinessLogic { // use the standard implementation of Random backed by java.util.Random() @@ -78,7 +77,7 @@ object Magic { Since `getMagicNumber` is not dependent on a particular implementation (it's referentially transparent), you can give it another instance of the type class as you see fit. -This is particularly useful when testing. In the following example, we need our `Random` implementation give back a stable value so we can ensure everything else works correctly, and our test assert succeeds. Since `randomizer` is passed into `getMagicNumber` we can swap it out in our test with a `Random` of which we can make stable. In our test implementation, calls to `betweenInt` will always give back `7`. This stability of "randomness" allows us to test that our function `getMagicNumber` does what we intend: +This is particularly useful when testing. In the following example, we need our `Random` implementation to give back a stable value so we can ensure everything else works correctly, and our test assert succeeds. Since `randomizer` is passed into `getMagicNumber`, we can swap it out in our test with a `Random` of which we can make stable. In our test implementation, calls to `betweenInt` will *always* give back `7`. This stability of "randomness" allows us to test that our function `getMagicNumber` does what we intend: @@ -88,8 +87,8 @@ import org.scalatest.matchers.must.Matchers.* class MagicSpec extends AnyFunSuite { - // for testing, create a Random instance that gives back the same number ever time. - // with a "stable" version of Random, we + // for testing, create a Random instance that gives back the same number every time. With + // this version of the Random type class, we can test our business logic works as intended. implicit val r: IO[Random[IO]] = IO( new Random[IO] { def betweenInt(minInclusive: Int, maxExclusive: Int): IO[Int] = From f15f3679b67f917e0271536926a6bfa4158b2086 Mon Sep 17 00:00:00 2001 From: Kevin Won Date: Wed, 6 Sep 2023 10:34:01 -0700 Subject: [PATCH 6/9] changed to explicit param --- docs/std/random.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/std/random.md b/docs/std/random.md index eeee7d00a3..1b99435f56 100644 --- a/docs/std/random.md +++ b/docs/std/random.md @@ -49,9 +49,9 @@ object BusinessLogic { // use the standard implementation of Random backed by java.util.Random() // (the same implementation as Random.javaUtilRandom(43)) - implicit val r: IO[Random[IO]] = Random.scalaUtilRandom[IO] + val randomizer: IO[Random[IO]] = Random.scalaUtilRandom[IO] - // other possible implemntations you could choose + // other possible implementations you could choose val sr = SecureRandom.javaSecuritySecureRandom(3) // backed java.security.SecureRandom() val jr = Random.javaUtilRandom(new java.util.Random()) // pass in the backing randomizer @@ -59,14 +59,15 @@ object BusinessLogic { // Doing it here to make the example easy to follow. def unsafeGetMessage: String = Magic - .getMagicNumber[IO](mult = 5) // instance of Random passed implicitly + .getMagicNumber[IO](5, randomizer) .unsafeRunSync() } object Magic { def getMagicNumber[F[_] : Monad]( - mult: Int - )(implicit randomizer: F[Random[F]]): F[String] = + mult: Int, + randomizer: F[Random[F]] + ): F[String] = for { rand <- randomizer.flatMap(random => random.betweenInt(1, 11)) // 11 is excluded number = rand * mult @@ -89,10 +90,10 @@ class MagicSpec extends AnyFunSuite { // for testing, create a Random instance that gives back the same number every time. With // this version of the Random type class, we can test our business logic works as intended. - implicit val r: IO[Random[IO]] = IO( + implicit val r: IO[Random[IO]] = IO.pure( new Random[IO] { def betweenInt(minInclusive: Int, maxExclusive: Int): IO[Int] = - IO(7) // gives back 7 every call + IO.pure(7) // gives back 7 every call // all other methods not implemented since they won't be called in our test def betweenDouble(minInclusive: Double, maxExclusive: Double): IO[Double] = ??? From 96ce8cc258abcd178d395f795e63391938081cdf Mon Sep 17 00:00:00 2001 From: Kevin Won Date: Wed, 27 Sep 2023 09:50:41 -0700 Subject: [PATCH 7/9] attemting to fix broken mdoc build --- docs/std/random.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/std/random.md b/docs/std/random.md index 1b99435f56..2cd3056ed0 100644 --- a/docs/std/random.md +++ b/docs/std/random.md @@ -80,9 +80,7 @@ Since `getMagicNumber` is not dependent on a particular implementation (it's ref This is particularly useful when testing. In the following example, we need our `Random` implementation to give back a stable value so we can ensure everything else works correctly, and our test assert succeeds. Since `randomizer` is passed into `getMagicNumber`, we can swap it out in our test with a `Random` of which we can make stable. In our test implementation, calls to `betweenInt` will *always* give back `7`. This stability of "randomness" allows us to test that our function `getMagicNumber` does what we intend: - - -```scala mdoc +```scala mdoc:silent import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.must.Matchers.* From 7bdd59d2ea5f010da40c6248e6d4c53369fb5841 Mon Sep 17 00:00:00 2001 From: Kevin Won Date: Wed, 18 Oct 2023 10:37:17 -0700 Subject: [PATCH 8/9] fix mdoc syntax --- docs/std/random.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/std/random.md b/docs/std/random.md index 2cd3056ed0..be8819d492 100644 --- a/docs/std/random.md +++ b/docs/std/random.md @@ -41,7 +41,7 @@ on the JVM: The following example shows the usage of `Random` in a way that is *referentially transparent*, meaning that you can know from the 'outside' what the result will be by knowing the inputs: ```scala mdoc:silent import cats.effect.std.Random -import cats.implicits.* +import cats.syntax.all import cats.Monad import cats.effect.unsafe.implicits.global @@ -82,7 +82,7 @@ This is particularly useful when testing. In the following example, we need our ```scala mdoc:silent import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.must.Matchers.* +import org.scalatest.matchers.must.Matchers._ class MagicSpec extends AnyFunSuite { From 62418da75e5f8c0f83c329b3c472e3a49248e9a7 Mon Sep 17 00:00:00 2001 From: Kevin Won Date: Wed, 18 Oct 2023 10:45:28 -0700 Subject: [PATCH 9/9] add wildcard --- docs/std/random.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/std/random.md b/docs/std/random.md index be8819d492..2a33a39898 100644 --- a/docs/std/random.md +++ b/docs/std/random.md @@ -41,7 +41,7 @@ on the JVM: The following example shows the usage of `Random` in a way that is *referentially transparent*, meaning that you can know from the 'outside' what the result will be by knowing the inputs: ```scala mdoc:silent import cats.effect.std.Random -import cats.syntax.all +import cats.syntax.all._ import cats.Monad import cats.effect.unsafe.implicits.global