diff --git a/dist/scala-with-cats.epub b/dist/scala-with-cats.epub index 8442e1c8..e79dd2cb 100644 Binary files a/dist/scala-with-cats.epub and b/dist/scala-with-cats.epub differ diff --git a/dist/scala-with-cats.html b/dist/scala-with-cats.html index 276214ea..801fca9b 100644 --- a/dist/scala-with-cats.html +++ b/dist/scala-with-cats.html @@ -9978,19 +9978,19 @@
randoms.take(5)
// res19: List[Double] = List(
-// 0.00579240678856785,
-// 0.27467053523994023,
-// 0.6168815655978637,
-// 0.07048256513105311,
-// 0.573129507199479
+// 0.9815709756062859,
+// 0.28209705240248073,
+// 0.17692738597312574,
+// 0.6583410813610281,
+// 0.22551887979099683
// )
randoms.take(5)
// res20: List[Double] = List(
-// 0.3805850631786687,
-// 0.5064645679068113,
-// 0.7876918555972987,
-// 0.8309416134228411,
-// 0.8366745103119307
+// 0.10647242182431838,
+// 0.9422816636323345,
+// 0.19684940126047634,
+// 0.7265388866125337,
+// 0.042925547854420154
// )
Now let’s define the same stream in a call by need style, using
lazy val
.
randomsByNeed.take(5)
// res21: List[Double] = List(
-// 0.03346048190683415,
-// 0.03346048190683415,
-// 0.03346048190683415,
-// 0.03346048190683415,
-// 0.03346048190683415
+// 0.997105748403435,
+// 0.997105748403435,
+// 0.997105748403435,
+// 0.997105748403435,
+// 0.997105748403435
// )
randomsByNeed.take(5)
// res22: List[Double] = List(
-// 0.03346048190683415,
-// 0.03346048190683415,
-// 0.03346048190683415,
-// 0.03346048190683415,
-// 0.03346048190683415
+// 0.997105748403435,
+// 0.997105748403435,
+// 0.997105748403435,
+// 0.997105748403435,
+// 0.997105748403435
// )
If we wanted a stream that had a different random number for each element but those numbers were constant, we could redefine @@ -10035,19 +10035,19 @@
randomsByNeed2.take(5)
// res23: List[Double] = List(
-// 0.9771900215781261,
-// 0.4383361680325434,
-// 0.6645095984529569,
-// 0.6719036485094727,
-// 0.8967127697079207
+// 0.32390565923174075,
+// 0.5842965840218141,
+// 0.4183464674983819,
+// 0.5083154197063308,
+// 0.7200935611895052
// )
randomsByNeed2.take(5)
// res24: List[Double] = List(
-// 0.9771900215781261,
-// 0.4383361680325434,
-// 0.6645095984529569,
-// 0.6719036485094727,
-// 0.8967127697079207
+// 0.32390565923174075,
+// 0.5842965840218141,
+// 0.4183464674983819,
+// 0.5083154197063308,
+// 0.7200935611895052
// )
These subtleties are one of the reasons that functional programmers try to avoid using state as far as possible.
@@ -11041,8 +11041,9 @@import JsonWriterInstances.given
summon[JsonWriter[String]]
-// res5: JsonWriter[String] = repl.MdocSession$MdocApp3$JsonWriterInstances$$anon$6@645f2f9d
+// res5: JsonWriter[String] = repl.MdocSession$MdocApp3$JsonWriterInstances$$anon$6@6b9dce2d
Most type classes in Cats provide other means to summon
instances. However, summon
is a good fallback for
debugging purposes. We can insert a call to summon
@@ -11486,7 +11487,7 @@
val showInt = Show.apply[Int]
-// showInt: Show[Int] = cats.Show$$$Lambda$18067/0x00007fdbbec82678@70483ba3
+// showInt: Show[Int] = cats.Show$$$Lambda$18084/0x00007fe706c91ed0@513af4a9
Oops—that didn’t work! The apply
method uses
implicits to look up individual instances, so we’ll have to
bring some instances into scope.
new Date().show
-// res1: String = "1716196208976ms since the epoch."
However, Cats also provides a couple of convenient methods to
simplify the process. There are two construction methods on the
companion object of Show
that we can use to define
@@ -14489,10 +14490,10 @@
val func = (x: Int) => x + 1
-// func: Function1[Int, Int] = repl.MdocSession$MdocApp0$$$Lambda$19546/0x00007fdbbee89a30@ea17f39
+// func: Function1[Int, Int] = repl.MdocSession$MdocApp0$$$Lambda$19544/0x00007fe706e8fa30@2a6cd408
val liftedFunc = Functor[Option].lift(func)
-// liftedFunc: Function1[Option[Int], Option[Int]] = cats.Functor$$Lambda$19547/0x00007fdbbee74688@61806c98
+// liftedFunc: Function1[Option[Int], Option[Int]] = cats.Functor$$Lambda$19545/0x00007fe706e95420@1b3cb92b
liftedFunc(Option(1))
// res1: Option[Int] = Some(value = 2)
@@ -15207,7 +15208,7 @@ val func3 = func1.map(func2)
-// func3: Function1[Int, Double] = scala.Function1$$Lambda$19496/0x00007fdbbee22118@26db7aad
Function1
has two type parameters (the function
argument and the result type):
trait Function1[-A, +B] {
@@ -15373,7 +15374,7 @@ val func2b: Double <= Double = func2
val func3c = func2b.contramap(func1)
-// func3c: Function1[Int, Double] = scala.Function1$$Lambda$19496/0x00007fdbbee22118@8a7924f
The difference between func2
and func2b
is purely syntactic—both refer to the same value and the type
aliases are otherwise completely compatible. Incredibly, however,
@@ -15913,7 +15914,7 @@
import scala.concurrent.ExecutionContext.Implicits.global
val fm = Monad[Future]
-// fm: Monad[[T >: Nothing <: Any] => Future[T]] = cats.instances.FutureInstances$$anon$1@1fc062b2
The Monad
instance uses the captured
ExecutionContext
for subsequent calls to
pure
and flatMap
:
This is an example of call-by-value evaluation:
These are the properties of call-by-name evaluation:
Let’s summarize. There are two properties of interest:
import cats.Eval
val now = Eval.now(math.random() + 1000)
-// now: Eval[Double] = Now(value = 1000.3648115892809)
+// now: Eval[Double] = Now(value = 1000.9650954739952)
val always = Eval.always(math.random() + 3000)
-// always: Eval[Double] = cats.Always@64643697
+// always: Eval[Double] = cats.Always@376da6ab
val later = Eval.later(math.random() + 2000)
-// later: Eval[Double] = cats.Later@6bcd9902
We can extract the result of an Eval
using its
value
method:
.value
- now// res6: Double = 1000.3648115892809
+// res6: Double = 1000.9650954739952
.value
- always// res7: Double = 3000.466869567198
+// res7: Double = 3000.2731813960345
.value
- later// res8: Double = 2000.0949690465334
Each type of Eval
calculates its result using one of
the evaluation models defined above. Eval.now
captures
a value right now. Its semantics are similar to a
@@ -16656,39 +16657,39 @@
Eval.always
captures a lazy computation, similar to
a def
:
val y = Eval.always{
println("Computing Y")
.random()
math}
-// y: Eval[Double] = cats.Always@388d61b1
+// y: Eval[Double] = cats.Always@68d500be
.value // first access
y// Computing Y
-// res12: Double = 0.4101650712443994 // first access
+// res12: Double = 0.4109022424918457 // first access
.value // second access
y// Computing Y
-// res13: Double = 0.4981025235006662
Finally, Eval.later
captures a lazy, memoized
computation, similar to a lazy val
:
val z = Eval.later{
println("Computing Z")
.random()
math}
-// z: Eval[Double] = cats.Later@f37a328
+// z: Eval[Double] = cats.Later@50b673d6
.value // first access
z// Computing Z
-// res14: Double = 0.6549553350257552 // first access
+// res14: Double = 0.8455416798865288 // first access
.value // second access
- z// res15: Double = 0.6549553350257552
The three behaviours are summarized below: