Does Scala have an equivalent to C# yield?

Viewed 5484

I'm new to Scala, and from what I understand yield in Scala is not like yield in C#, it is more like select.

Does Scala have something similar to C#'s yield? C#'s yield is great because it makes writing iterators very easy.

Update: here's a pseudo code example from C# I'd like to be able to implement in Scala:

public class Graph<T> {
   public IEnumerable<T> BreadthFirstIterator() {
      List<T> currentLevel = new List<T>();
      currentLevel.add(_root);

      while ( currentLevel.count > 0 ) {
         List<T> nextLevel = new List<T>();
         foreach( var node in currentLevel ) {
            yield return node;
            nextLevel.addRange( node.Children );
         }
         currentLevel = nextLevel;
      }
   }
}

This code implements an iterative breadth first traversal of a graph, using yield, it returns an iterator, so that callers can traverse the graph using a regular for loop, e.g.:

graph.BreadthFirstIterator().foreach( n => Console.WriteLine( n ) );

In C#, yield is just syntactic sugar to make it easy to write an iterator (IEnumerable<T> in .Net, similar to Iterable in Java). As an iterator, its evaluated lazily.

Update II: I could be wrong here, but I think the whole point of yield in C# is so that you don't have to write a higher order function. E.g. you can write a regular for loop or use a method like select/map/filter/where instead of passing in a function which will then traverse the sequence.

E.g. graph.iterator().foreach(n => println(n)) instead of graph.iterator( n => println(n)).

This way you can chain them easily, e.g graph.iterator().map(x => x.foo).filter(y => y.bar >= 2).foreach(z => println(z)).

7 Answers

You can do this in Scala >= 2.8 using an implementation of generators in terms of delimited continuations. You'll need the continuations plugin and then something along these lines,

import scala.continuations._
import scala.continuations.ControlContext._

object Test {

  def loopWhile(cond: =>Boolean)(body: =>(Unit @suspendable)): Unit @suspendable = {
    if (cond) {
      body
      loopWhile(cond)(body)
    } else ()
  }

  abstract class Generator[T] {
    var producerCont : (Unit => Unit) = null
    var consumerCont : (T => Unit) = null

    protected def body : Unit @suspendable

    reset {
      body
    }

    def generate(t : T) : Unit @suspendable =
      shift {
        (k : Unit => Unit) => {
          producerCont = k
          if (consumerCont != null)
            consumerCont(t)
        }
      }

    def next : T @suspendable =
      shift {
        (k : T => Unit) => {
          consumerCont = k
          if (producerCont != null)
            producerCont()
        }
      }
  }

  def main(args: Array[String]) {
    val g = new Generator[Int] {
      def body = {
        var i = 0
        loopWhile(i < 10) {
          generate(i)
          i += 1
        }
      }
    }

    reset {
      loopWhile(true) {
        println("Generated: "+g.next)
      }
    }
  }
}
Related