Question

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

Was it helpful?

Solution 3

I think the answer (barring changes in 2.8) is that the answer is no, Scala does not have syntactic sugar similar to C#'s yield to write iterators (implementations of IEumerable or Iterable).

However, in Scala you could instead achieve a similar result by passing in a function to the traversal which it would invoke on each item in the traversal. This approach could also be implemented in the same fashion in C#.

Here is how I'd write Traverse in C# without the use of yield:

public class Graph<T> {
   public void BreadthFirstTraversal( Action<T> f) {
      List<T> currentLevel = new List<T>();
      currentLevel.add(_root);

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

You could then use it like this:

graph.BreadthFirstTraversal( n => Console.WriteLine( n ) );

Or like this:

graph.BreadthFirstTraversal( n =>
{
   Console.WriteLine(n);
   DoSomeOtherStuff(n);
});

OTHER TIPS

The hijacking of the word yield here distracts from its usual intent: as an entry/exit marker in a coroutine. The C# BreadthFirstIterator in the example above appears to use yield in its coroutine sense; after a value is returned by yield, the next call to active BreadthFirstIterator's IEnumerable will continue with the next statement after yield.

In C#, yield is coupled to the idea of iteration rather than being a more general control flow statement, but within that limited domain its behavior is that of a coroutine. Scala's delimited continuations may allow one to define coroutines. Until then, Scala lacks such a capability, especially given its alternate meaning for yield.

Yes it does, you may want to look at this question for the answer: What is Scala's yield?

Here is the docs from Scala for this type of construct: http://www.scala-lang.org/node/111

UPDATE:

This blog talks about C# yield and Scala: http://hestia.typepad.com/flatlander/2009/01/scala-for-c-programmers-part-1-mixins-and-traits.html

He goes into some detail about how extensions are being used to make IENumerable work compared to using Traits in Scala.

So, you are correct that yield won't function the same way in Scala as C#, but that is because they are very different, and so if you want to do this BreadthFirst as a Trait then you can call the map() and filter and foreach methods, just as you would in C#, but the trait will help solve the problem of how to traverse the collection.

Even though Scala has a keyword yield, it's quite different from the C# yield, and Ruby's yield is different from both. It seems to be a wildly overused keyword. The use of yield in C# appears very limited at first glance.

To do the same in Scala, you could define your own high-order function. In English, that means a function that takes a function as a parameter.

To take Microsoft's example, here's a Scala method:

object Powers {
  def apply(number:Int, exponent:Int) (f:(Double) => Any) = {
    (new Range(1,exponent+1,1)).map{exponent => f(Math.pow(number, exponent))}
  }
}

Now you have your "iterator":

scala> Powers(2,8){ println(_) }
2.0
4.0
8.0
16.0
32.0
64.0
128.0
256.0

Notes:

  • Powers(2,8) is the same as Powers.apply(2,8). That's just a compiler trick.
  • This method is defined with two parameter lists, which might be confusing. It just allows you to do: Powers(2, 8){ println(_) } instead of Powers(2, 8, {println(_)})

Scala: 1, C#: 0


Update:

For your just-added example, write traverse that does the traversal you want without thinking about how you are going to use it. Then add an extra parameter by adding (f(Node) => Any) after the traverse parameter list, e.g.

def traverse(node:Node, maxDepth:Int)(f(Node) => Any)) { ... }

At the point in traverse where you have a value you would yield with in C#, call f(yieldValue).

When you want to use this "iterator," call traverse and pass a function to it that does whatever it is you want to do for each element in the iterator.

traverse(node, maxDepth) { (yieldValue) =>
  // this is f(yieldValue) and will be called for each value that you call f with
  println(yieldValue)
}

This is a basic case for "functional programming" and you should make sure you understand it to be successful with Scala.

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

As already mentioned you could create a Generator using the continuations-plugin to create a yield which is exactly behaving like C#:

import scala.util.continuations._

object GenTest {

    val gen = new Generator[Int] { def produce = {
        yieldValue(1)
        yieldValue(2)
        yieldValue(3)
        Thread.sleep(1000)
        yieldValue(42)
  }}


    def main(args: Array[String]): Unit = {
        for (v <- gen) {
            println(v)
        }
    }
}

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): Unit @cps[Unit] =
    shift { genK: (Unit => Unit) =>
      loopFn( value )
      genK( () )
      ()
    }

}

Coming from a C# background and having debugged the Scala code from hotzen(adapted to Scala 2.11.6), I must say this continuations usage comes close to the C#-yield equivalent. I do not know if continuations still would function similarly if multiple Generators were needed, running all in the same methods or possibly spread over different methods, but I am happy continuations do exist, so that I am not forced to work with multiple threads to achieve similar, or pass along call-backs.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top