延续和推导式——有什么不兼容之处?

发布于 2024-12-27 17:46:15 字数 1890 浏览 3 评论 0原文

我是 Scala 新手,并试图了解延续性 我正在尝试重现 yield return C# 语句。

这篇文章之后,我编写了以下代码:

package com.company.scalatest

import scala.util.continuations._;

object GenTest {

  val gen = new Generator[Int] {
    def produce = {
      yieldValue(1)
      yieldValue(2)
      yieldValue(3)
      yieldValue(42)
    }
  }
  // Does not compile :(

  //  val gen2 = new Generator[Int] {
  //    def produce = {
  //      var ints = List(1, 2, 3, 42);
  //
  //      ints.foreach((theInt) => yieldValue(theInt));
  //    }
  //  }

  // But this works?
  val gen3 = new Generator[Int] {
    def produce = {
      var ints = List(1, 2, 3, 42);
      var i = 0;
      while (i < ints.length) {
        yieldValue(ints(i));
        i = i + 1;
      }
    }
  }

  def main(args: Array[String]): Unit = {
    gen.foreach(println);
    //    gen2.foreach(println);
    gen3.foreach(println);
  }
}

abstract class Generator[E] {

  var loopFn: (E => Unit) = null

  def produce(): Unit @cps[Unit]

  def foreach(f: => (E => Unit)): Unit = {
    loopFn = f
    reset[Unit, Unit](produce)
  }

  def yieldValue(value: E) =
    shift { genK: (Unit => Unit) =>
      loopFn(value)
      genK(())
      ()
    }
}

如您所见,gen2被注释因为它无法编译。由于我可以使用 while 循环轻松迭代列表的内容(请参阅 gen3 ),因此我预计 foreach 循环也能正常工作。

编译错误如下:

no type parameters for method foreach: (f: Int => B)Unit exist so that 
it can be applied to arguments (Int => Unit @scala.util.continuations.cpsParam[Unit,Unit])  
 --- because --- 
argument expression's type is not compatible with formal parameter type;  
found   : Int => Unit @scala.util.continuations.cpsParam[Unit,Unit]  
required: Int => ?B 

为什么我会收到此错误,有没有办法用比 while 循环更干净的方法来解决此问题?

谢谢

I am new to Scala and trying to wrap my head around continuations
I'm trying to reproduce the yield return C# statement.

Following this post, I have written the following code :

package com.company.scalatest

import scala.util.continuations._;

object GenTest {

  val gen = new Generator[Int] {
    def produce = {
      yieldValue(1)
      yieldValue(2)
      yieldValue(3)
      yieldValue(42)
    }
  }
  // Does not compile :(

  //  val gen2 = new Generator[Int] {
  //    def produce = {
  //      var ints = List(1, 2, 3, 42);
  //
  //      ints.foreach((theInt) => yieldValue(theInt));
  //    }
  //  }

  // But this works?
  val gen3 = new Generator[Int] {
    def produce = {
      var ints = List(1, 2, 3, 42);
      var i = 0;
      while (i < ints.length) {
        yieldValue(ints(i));
        i = i + 1;
      }
    }
  }

  def main(args: Array[String]): Unit = {
    gen.foreach(println);
    //    gen2.foreach(println);
    gen3.foreach(println);
  }
}

abstract class Generator[E] {

  var loopFn: (E => Unit) = null

  def produce(): Unit @cps[Unit]

  def foreach(f: => (E => Unit)): Unit = {
    loopFn = f
    reset[Unit, Unit](produce)
  }

  def yieldValue(value: E) =
    shift { genK: (Unit => Unit) =>
      loopFn(value)
      genK(())
      ()
    }
}

As you can see, gen2 is commented out as it does not compile. Since I can easily iterate over the content of a list using a while loop (see gen3), I expected the foreach loop to work just as well.

The compilation error is the following :

no type parameters for method foreach: (f: Int => B)Unit exist so that 
it can be applied to arguments (Int => Unit @scala.util.continuations.cpsParam[Unit,Unit])  
 --- because --- 
argument expression's type is not compatible with formal parameter type;  
found   : Int => Unit @scala.util.continuations.cpsParam[Unit,Unit]  
required: Int => ?B 

Why do I get this error and is there a way to work around this with something cleaner than a while loop?

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

于我来说 2025-01-03 17:46:15

首先让我们看看如何编译 gen2

object CpsConversions {

  import scala.collection.IterableLike
  import scala.util.continuations._

  implicit def cpsIterable[A, Repr](xs: IterableLike[A, Repr]) = new {
    def cps = new {
      def foreach[B](f: A => Any@cpsParam[Unit, Unit]): Unit@cpsParam[Unit, Unit] = {
        val it = xs.iterator
        while(it.hasNext) f(it.next)
      }
    }
  }
}

object GenTest {

  import CpsConversions.cpsIterable
  val gen2 = new Generator[Int] {
    def produce = {
      var ints = List(1, 2, 3, 42)
      ints.cps.foreach((theInt) => yieldValue(theInt))
    }
  }

现在让我们看看到底发生了什么。原始的 gen2 无法在以下行编译:

ints.foreach((theInt) => yieldValue(theInt))

由于 yieldValue 的类型包含 @cpsParam 注释,延续插件会转换传递的函数将 foreach 方法转换为以下类型之一:

Int => Unit @cpsParam[Unit,Unit]

List[Int] 的层次结构中,您会看到 foreach 定义为

foreach [U] (f: (Int) ⇒ U): Unit

:是一个问题,因为类型不匹配Scala 不知道如何从 Int => 获取UInt =>单位@cpsParam[单位,单位]。为了解决这个问题,我在隐式转换中添加了 CPS 版本的 foreach,您可以通过在任何 IterableLike 上调用 cps 来访问它。

如果这种隐式转换可以在没有显式 cps 调用的情况下完成,那就太好了,但我还没有找到一种方法让 Scala 编译器认识到这种隐式转换对新 cps 调用的适用性。 code>foreach 到您的列表中。这可能与编译器使用延续插件的顺序有关,但我对这个过程知之甚少,无法确定。

所以对于 foreach 来说这一切都很好。您的问题提到了理解,这将需要定义 filtermapflatMap 中的任何一个(取决于您的 for 中发生的情况)理解)。我已经在上面评论的链接中实现了这些,它扩展了上面的 CpsConversions 对象以允许一般的理解。

First let's look at what it will take to get gen2 to compile.

object CpsConversions {

  import scala.collection.IterableLike
  import scala.util.continuations._

  implicit def cpsIterable[A, Repr](xs: IterableLike[A, Repr]) = new {
    def cps = new {
      def foreach[B](f: A => Any@cpsParam[Unit, Unit]): Unit@cpsParam[Unit, Unit] = {
        val it = xs.iterator
        while(it.hasNext) f(it.next)
      }
    }
  }
}

object GenTest {

  import CpsConversions.cpsIterable
  val gen2 = new Generator[Int] {
    def produce = {
      var ints = List(1, 2, 3, 42)
      ints.cps.foreach((theInt) => yieldValue(theInt))
    }
  }

Now let's take a look at what's going on. The original gen2 fails to compile on the following line:

ints.foreach((theInt) => yieldValue(theInt))

Since the type of yieldValue includes an @cpsParam annotation, the continuations plugin transforms the function passed to the foreach method to one of type:

Int => Unit @cpsParam[Unit,Unit]

Way up in the hierarchy of List[Int], you'll see foreach defined as:

foreach [U] (f: (Int) ⇒ U): Unit

This is a problem, as the types do not match and Scala doesn't know how to get from Int => U to Int => Unit @cpsParam[Unit,Unit]. To fix it, I added the CPS version of foreach in an implicit conversion, which you can access by calling cps on any IterableLike.

It would be very nice if this implicit conversion could be done without the explicit cps call, but I have not found a way to make the Scala compiler recognize the applicability of such an implicit conversion to pimp the new foreach onto your list. This might have to do with the order in which the compiler uses the continuations plugin, but I know far too little about this process to be sure.

So that's all well and good for foreach. Your question mentions for comprehensions, which will require any of filter, map, or flatMap to be defined (depending on what goes on in your for comprehension). I have implemented these in the link in my above comment, which extends the CpsConversions object above to allow for general for comprehensions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文