For-Comprehensions

All containers fully support Scala language "for-comprehensions" notation. There is one twist though, which must be kept in mind. Collections delegate functional processing to the Stream, thus whenever a collection is included in "for-comprehension" it is automatically converted to Stream.

    val packOfInts: Pack[Int] = (1 <> 10).stream.pack

    var sum = 0

    for(i <- packOfInts if i < 5) sum += i    

    // Explicit .stream call would produce exactly same compiled code 

    for(i <- packOfInts.stream if i < 5) sum += i    

The usage is different from Scala, when one needs to yield the result as a collection, the Stream has to be converted explicitly:

    val seq: Seq[String] =   for(i <- 1 until 10 ) yield i.tag

    val idx: Idx[String] = { for(i <- 1 <>> 10)    yield i.tag }.toIdx  // Explicit stream conversion  

Single value containers: Opt, Result, and Promise are processed and yielded as their own type. This is exactly same as Option, Try, and Future in Scala.

Specialized

Containers, when used in "for-comprehensions", are still specialized for primitives:

    val array: Array[Int] = (1 <> 1_000_000).stream.toArray

    J.Benchmark(
      ("Array ", () => { var s=0L;  for(i <- array        if i%2 == 0) s += i; s }),
      ("Stream", () => { var s=0L;  for(i <- array.stream if i%2 == 0) s += i; s }),
    )

    // Output
    Final Result. Total length is about 12 secs
    --- ------- ------- --- ------ --- -----------
    Num Name    Ops/Sec %   Memory %   Avg Value
    --- ------- ------- --- ------ --- -----------
    1   Array   69      53  15.8mB 100 2.500005E11
    2   Stream  130     100 5.3kB  0   2.500005E11
    --- ------- ------- --- ------ --- -----------

Memory consumption clearly indicates unboxed processing.

Perfect Int Loop

Int.Range is special. When used in simple "for-comprehension" with foreach logic, a direct Java for-loop is produced. Some macro magic is obviously involved.

    var a = new Array[Int](1000)
    for(i <- 0 <>> a.length) a(i) = i

gets compiled into Java "for":

    int[] a = (int[])(new int[1000]);
    int i = 0;

    for(int e = a.length; i < e; ++i) {
        a[i] = i;
    }

with ultimate performance as a result:

    val a: Array[Int] = new Array(1000)

    J.Benchmark(
      ("Range    ", () => { for(i <- 0 until a.length) a(i)=i; a.length }),
      ("Int.Range", () => { for(i <- 0 <>> a.length  ) a(i)=i; a.length }),
    )

    // Output
    Final Result. Total length is about 12 secs
    --- --------- ------- --- ------ --- ---------
    Num Name      Ops/Sec %   Memory %   Avg Value
    --- --------- ------- --- ------ --- ---------
    1   Range     126.5k  3   14.1kB 100 1000.0
    2   Int.Range 3.4m    100 6B     0   1000.0
    --- --------- ------- --- ------ --- ---------