延续和理解-不兼容是什么? [英] Continuations and for comprehensions -- what's the incompatibility?

查看:130
本文介绍了延续和理解-不兼容是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Scala的新手,并试图将自己的头缠在延续上 我正在尝试重现yield return C#语句.

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(())
      ()
    }
}

如您所见,由于gen2未编译,因此已被注释掉.由于我可以使用while循环轻松访问列表的内容(请参见gen3),因此我希望foreach循环也能正常工作.

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.

编译错误如下:

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循环更干净的方法来解决此问题?

谢谢

推荐答案

首先让我们看一下gen2进行编译的过程.

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))
    }
  }

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

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))

由于yieldValue的类型包括@cpsParam批注,所以continuation插件将传递给foreach方法的函数转换为以下类型之一:

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]

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

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

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

这是一个问题,因为类型不匹配并且Scala不知道如何从Int => UInt => Unit @cpsParam[Unit,Unit].为了解决此问题,我在隐式转换中添加了foreach的CPS版本,您可以通过在任何IterableLike上调用cps进行访问.

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.

如果不用显式的cps调用就可以完成这种隐式转换,那将是非常好的,但是我还没有找到一种使Scala编译器认识到这种隐式转换对新的foreach皮条客的适用性的方法.到您的列表中.这可能与编译器使用continuation插件的顺序有关,但是我对这一过程了解得很少,无法确定.

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.

所以对于foreach来说一切都很好.您的问题提到了理解,这将要求定义filtermapflatMap中的任何一个(取决于您对理解的理解).我已经在上面的评论的链接中实现了这些内容,该链接扩展了上面的CpsConversions对象以允许进行一般理解.

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.

这篇关于延续和理解-不兼容是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆