【问题标题】:Why is Deferred factory method has return value in the context of F为什么延迟工厂方法在F的上下文中有返回值
【发布时间】:2019-07-18 20:28:16
【问题描述】:

我正在查看 cats.effect.concurrent.Deferred 并注意到其伴随对象中的所有 pure 工厂方法都返回 F[Deferred[F, A]],而不仅仅是 Deferred[F, A] 之类的 p>

def apply[F[_], A](implicit F: Concurrent[F]): F[Deferred[F, A]] =
  F.delay(unsafe[F, A])

但是

  /**
    * Like `apply` but returns the newly allocated promise directly instead of wrapping it in `F.delay`.
    * This method is considered unsafe because it is not referentially transparent -- it allocates
    * mutable state.
    */
  def unsafe[F[_]: Concurrent, A]: Deferred[F, A]

为什么?

abstract class 定义了两个方法(文档省略):

abstract class Deferred[F[_], A] {
  def get: F[A]
  def complete(a: A): F[Unit]
}

因此,即使我们直接分配Deferred,也不清楚如何通过其公共方法修改Deferred 的状态。使用F[_] 暂停所有修改。

【问题讨论】:

    标签: scala functional-programming referential-transparency cats-effect


    【解决方案1】:

    问题不在于突变是否在F 中暂停,而在于Deferred.unsafe 是否允许您编写不透明的代码。考虑以下两个程序:

    import cats.effect.{ContextShift, IO}
    import cats.effect.concurrent.Deferred
    import cats.implicits._
    import scala.concurrent.ExecutionContext
    
    implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
    
    val x = Deferred.unsafe[IO, Int]
    
    val p1 = x.complete(1) *> x.get
    val p2 = Deferred.unsafe[IO, Int].complete(1) *> Deferred.unsafe[IO, Int].get
    

    这两个程序不等价:p1 将计算 1p2 将永远等待。我们可以构造这样的示例这一事实表明 Deferred.unsafe 不是引用透明的——我们不能随意用引用替换对它的调用并最终得到等效的程序。

    如果我们尝试对Deferred.apply 做同样的事情,我们会发现我们无法通过将引用替换为值来生成一对不等价的程序。我们可以试试这个:

    val x = Deferred[IO, Int]
    
    val p1 = x.flatMap(_.complete(1)) *> x.flatMap(_.get)
    val p2 = Deferred[IO, Int].flatMap(_.complete(1)) *> Deferred[IO, Int].flatMap(_.get)
    

    ...但这给了我们两个等效的程序(都挂起)。即使我们尝试这样的事情:

    val x = Deferred[IO, Int]
    
    val p3 = x.flatMap(x => x.complete(1) *> x.get)
    

    ...所有引用透明性都告诉我们,我们可以将代码重写为以下内容:

    val p4 = Deferred[IO, Int].flatMap(x => x.complete(1) *> x.get)
    

    ...相当于p3,所以我们未能再次打破引用透明性。

    当我们使用Deferred.apply 时,我们无法在F 的上下文之外获得对可变Deferred[IO, Int] 的引用,这一事实正是在这里保护我们的原因。

    【讨论】:

    • 这样,任何创建封装可变状态的实例的方法(即使通过在F[_] 中暂停突变的方法访问可变状态)都不能被视为引用透明。对吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-29
    • 2019-03-15
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多