« Return to Thread: [scala] Simulating python yield with scala continuations

Re: [scala] Simulating python yield with scala continuations

by Jorge Ortiz-3 :: Rate this Message:

Reply to Author | View in Thread

Scala has lazy collections, which, AFAIK, can do whatever generators can without needing the continuations plugin.

  val generate =
    (
      Array("first").projection append
      (1 to 4).map(_.toString) append
      Array("last").projection
    ).elements

  for (r <- generate) println(r)

The code is, admittedly, a little ugly, but could be made a little cleaner with 2.8 collections.

(Note this won't work in the interpreter, as the interpreter calls toString on "generate", which forces its evaluation. If you suppress the call to toString, it works as specified.)

--j

On Wed, Jun 17, 2009 at 7:17 PM, Arnold deVos <adv-list-scala@...> wrote:
I'm interested in the upcoming continuations support so I thought I would try
to build something similar to the python generators with it.  Here is a scrap
of python:

def generate():
   yield "first"
   for i in range(1,4):
       yield str(i)
   yield "last"

In python this function returns an iterator over the values passed to the
yield statements. The nice thing is: it is lazy and can be used as a
coroutine.  Anyway, if we iterate like this:

for r in generate():
   print r

It prints this:

first
1
2
3
last

Here is a scala version in which a produce() method serves the same role as
python's yield:

object Generator {
   def generate = {
       val g = new Generator[String]
       reset {
           g produce "first"
           g produce 1.toString
           g produce 2.toString
           g produce 3.toString
           g produce "last"
       }
       g
   }

   def main(args: Array[String]) {
       for( a <- generate ) println(a)
   }
}

This also prints

first
1
2
3
last

The Generator class is defined in terms of shift() as follows:

class Generator[A] extends Iterator[A] {
   private var a: A = _
   private var k: (Unit => Unit) = null

   def next = {
       val a0 = a
       val k0 = k
       k = null
       k0()
       a0
   }

   def hasNext = k != null

   def produce(a0: A): Unit @ suspendable = {
       a = a0
       shift { k0: (Unit => Unit) => k = k0 }
   }
}

But the example is not quite the same as the python because that had a loop in
the generate function.  Lets try that in the scala version, replacing the
generate method with:

   def generate = {
       val g = new Generator[String]
       reset {
           g produce "first"
           for( i <- 1 to 4) { g produce i.toString }
           g produce "last"
       }
       g
   }

But this does not compile:

generator.scala:28: error: type mismatch;
 found   : (Int) => Unit @scala.continuations.cps[Unit,Unit]
 required: (Int) => Unit
           for( i <- 1 to 4) { g produce i.toString }
                ^
one error found

Substituting a while loop produces a different compile error. Do any
continuation experts/experimenters out there know what the problem is?

The example was compiled with scala r17271 plus the latest continuations
plugin from trunk as of r18035 for reasons explained in the previous post.
This may be the problem, I don't know.

- Arnold


 « Return to Thread: [scala] Simulating python yield with scala continuations