Monads 是一种排序计算的机制,下面的列表仍然是 monad,尽管它们是以随机顺序打印的 Scala
Monads being a mechanism for sequencing computations, is the below list still a monad though they are printed in a random order Scala
for {
i <- 1 to 5
} yield Future(println(i))
脱糖至:
List(1,2,3,4,5).map {i => Future(println(i))}
以上代码以随机顺序打印数字。
现在,如果我们看到 Monad 的多个定义:
a) Monad 是对象的包装器
b) Monad 是一种排序计算的机制
我想回答的问题是,List monad 上的 map 操作不应该等待列表中的第一个元素被打印出来,然后才去计算第二个元素而不考虑 Future?
抱歉,这可能很简单,我把它复杂化了,但找到简单的推理对我来说变得更加棘手。答案将不胜感激:)
map 的执行确实是顺序的,但是当你将它包装到 Future 时,它会以异步方式执行,我的意思是它是在另一个线程中计算的,因此,不可能知道是哪个线程会提前结束,因为还要看操作系统的线程管理等方面的考虑。
比较:
for {
_ <- Future(println(1))
_ <- Future(println(2))
_ <- Future(println(3))
_ <- Future(println(4))
_ <- Future(println(5))
} yield ()
或
Future(println(1)).flatMap { _ =>
Future(println(2))
}.flatMap { _ =>
Future(println(3))
}.flatMap { _ =>
Future(println(4))
}.flatMap { _ =>
Future(println(5))
}
和
List(
Future(println(1)),
Future(println(2)),
Future(println(3)),
Future(println(4)),
Future(println(5))
)
前两个仅在前者完成并使结果可用后才创建下一个 Future
。最后一个一次创建所有 Future
(在这方面与您使用 List[Future]
的示例没有太大区别)。
Future
(与 Cats Effect 中的 IO
、Monix 的 Task
或 ZIO 相对)是急切的,因此它会在您创建它的那一刻开始执行。出于这个原因,前两个示例中有顺序结果,第三个示例中有随机顺序(竞争条件)。
如果您使用 IO
而不是 Future
它会更明显,因为您不能只使用 List[IO[Unit]]
并执行副作用 - 您将不得不以某种方式将不同的 IOs 合二为一,您将采用的方式可以清楚地表明效果是连续的还是平行的。
底线是 - Future
是否是 monad 取决于 .flatMap
的行为方式(以及它与 Future.successful
结合时的行为方式),因此您的结果不't 使 Future
是 monad 的声明无效。 (如果你开始检查它的异常行为,你可能会有一些疑问,但那是另一个话题)。
从广义上讲,您的两个代码片段仍然是 Monad。当您对对象执行 .map()
时,地图会按顺序(从索引 0 到索引 4)一个接一个地选取元素。然后它将其传递给一个操作块(它是 map 的主体 - map 是一个高阶函数,它接受类型为 f:This => That 的函数)。
所以 monad 操作的职责是获取它并将其作为参数传递给函数。
在你的例子中,实际函数类型是:
f: Int => Future[Unit]
为清楚起见,您的函数实际上如下所示:
def someFunction(i: Int): Future[Unit] = {
Future {
println(i)
}
}
因此,地图操作在这里所做的是它从您的对象中挑选项目(按顺序,一个接一个)并调用 someFunction(i)。这就是 monad 所做的一切。
现在回答为什么你的 println 是随机的,这是因为 JVM 线程。
如果您像这样重新定义地图主体
List(1,2,3,4,5)
.map {i =>
println(s"Going to invoke the println in another thread for $i")
Future(println(i))
}
您会看到第一个 println
将按顺序排列 - 始终如此!它证明 .map()
按顺序选择你的元素。而下一个 println
可能会或可能不会乱序。这种无序的时尚不是因为 monad 操作 map
,而是因为多核 CPU 中的多线程特性。
for {
i <- 1 to 5
} yield Future(println(i))
脱糖至:
List(1,2,3,4,5).map {i => Future(println(i))}
以上代码以随机顺序打印数字。
现在,如果我们看到 Monad 的多个定义: a) Monad 是对象的包装器 b) Monad 是一种排序计算的机制
我想回答的问题是,List monad 上的 map 操作不应该等待列表中的第一个元素被打印出来,然后才去计算第二个元素而不考虑 Future?
抱歉,这可能很简单,我把它复杂化了,但找到简单的推理对我来说变得更加棘手。答案将不胜感激:)
map 的执行确实是顺序的,但是当你将它包装到 Future 时,它会以异步方式执行,我的意思是它是在另一个线程中计算的,因此,不可能知道是哪个线程会提前结束,因为还要看操作系统的线程管理等方面的考虑。
比较:
for {
_ <- Future(println(1))
_ <- Future(println(2))
_ <- Future(println(3))
_ <- Future(println(4))
_ <- Future(println(5))
} yield ()
或
Future(println(1)).flatMap { _ =>
Future(println(2))
}.flatMap { _ =>
Future(println(3))
}.flatMap { _ =>
Future(println(4))
}.flatMap { _ =>
Future(println(5))
}
和
List(
Future(println(1)),
Future(println(2)),
Future(println(3)),
Future(println(4)),
Future(println(5))
)
前两个仅在前者完成并使结果可用后才创建下一个 Future
。最后一个一次创建所有 Future
(在这方面与您使用 List[Future]
的示例没有太大区别)。
Future
(与 Cats Effect 中的 IO
、Monix 的 Task
或 ZIO 相对)是急切的,因此它会在您创建它的那一刻开始执行。出于这个原因,前两个示例中有顺序结果,第三个示例中有随机顺序(竞争条件)。
如果您使用 IO
而不是 Future
它会更明显,因为您不能只使用 List[IO[Unit]]
并执行副作用 - 您将不得不以某种方式将不同的 IOs 合二为一,您将采用的方式可以清楚地表明效果是连续的还是平行的。
底线是 - Future
是否是 monad 取决于 .flatMap
的行为方式(以及它与 Future.successful
结合时的行为方式),因此您的结果不't 使 Future
是 monad 的声明无效。 (如果你开始检查它的异常行为,你可能会有一些疑问,但那是另一个话题)。
从广义上讲,您的两个代码片段仍然是 Monad。当您对对象执行 .map()
时,地图会按顺序(从索引 0 到索引 4)一个接一个地选取元素。然后它将其传递给一个操作块(它是 map 的主体 - map 是一个高阶函数,它接受类型为 f:This => That 的函数)。
所以 monad 操作的职责是获取它并将其作为参数传递给函数。
在你的例子中,实际函数类型是:
f: Int => Future[Unit]
为清楚起见,您的函数实际上如下所示:
def someFunction(i: Int): Future[Unit] = {
Future {
println(i)
}
}
因此,地图操作在这里所做的是它从您的对象中挑选项目(按顺序,一个接一个)并调用 someFunction(i)。这就是 monad 所做的一切。
现在回答为什么你的 println 是随机的,这是因为 JVM 线程。
如果您像这样重新定义地图主体
List(1,2,3,4,5)
.map {i =>
println(s"Going to invoke the println in another thread for $i")
Future(println(i))
}
您会看到第一个 println
将按顺序排列 - 始终如此!它证明 .map()
按顺序选择你的元素。而下一个 println
可能会或可能不会乱序。这种无序的时尚不是因为 monad 操作 map
,而是因为多核 CPU 中的多线程特性。