Playing with streams

Showing 1-2 of 2 messages
Playing with streams wwagner4 8/24/12 6:30 AM
object StreamsTryout extends App {

  // What basic function exist to create a Stream
  {
    val s = Stream.from(-1)
    println("(1)" + take10(s))
  }
  {
    val s = Stream.from(100, -3)
    println("(2)" + take10(s))
  }
  {
    val s = Stream.continually("A")
    println("(3)" + take10(s))
  }
  {
    val s = "B" #:: Stream.continually("A")
    println("(4)" + take10(s))
  }
  // Add a separator except for the first item using the [false, true, true, true, ...] stream s1
  {
    val s1 = false #:: Stream.continually(true)
    val s2 = List("A", "B", "C")
    val s3 = s2 zip s1
    val l = (s3 map { case (v, b) => if (b) List("|", v) else List(v) }).flatten mkString ("(", "", ")")
    println("(5a)" + l)
  }
  // The [false, true, true, true, ...] stream s1 can be reused in different threads at the same time. It is immutable
  {
    val s1 = false #:: Stream.continually(true)
    scala.actors.Actor.actor {
      val s2 = 1 to 5
      val s3 = s2 zip s1
      val l = (s3 map { case (v, b) => if (b) List("|", v) else List(v) }).flatten mkString ("(", "", ")")
      println("(5b)" + l)
    }
    scala.actors.Actor.actor {
      val s2 = List("A", "B", "C")
      val s3 = s2 zip s1
      val l = (s3 map { case (v, b) => if (b) List("|", v) else List(v) }).flatten mkString ("(", "", ")")
      println("(5c)" + l)
    }
    scala.actors.Actor.actor {
      val s2 = 1 to 200
      val s3 = s2 zip s1
      val l = (s3 map {case (v, true) => List("|", v) case (v, _) => List(v) }).flatten mkString ("(", "", ")")
      println("(5d)" + l)
    }
    scala.actors.Actor.actor {
      val s2 = 1 to 200
      val s3 = s2 zip s1
      val l = (s3 map {case (v, true) => List("|", v) case (v, _) => List(v)}).flatten mkString ("(", "", ")")
      println("(5e)" + l)
    }
  }
  {
    // How can that be written in one line ?
    val s1: Stream[Option[String]] = Stream.continually(Some("A"))
    val s = None #:: s1
    println("(6)" + take10(s))
  }
  // Iterate is for me the most interesting method of creating streams
  {
    val s = Stream.iterate(2) { n => n + 3 }
    println("(7)" + take10(s))
  }
  {
    val s = Stream.iterate(List.fill(4)(100)) { n => n map (_ + 1) }
    println("(8)" + take10(s))
  }
  {
    val s = Stream.iterate(List.fill(4)(-4)) { n => n map (_ + 1) }.flatten
    println("(9)" + take10(s))
  }
  {
    val s = Stream.continually(0 until 4)
    println("(10)" + take10(s))
  }
  {
    val s = Stream.continually(0 until 4).flatten
    println("(11)" + take10(s))
  }
  // Coordinates for an n columns array produced with two streams s1, s2
  {
    val n = 3
    val s1 = Stream.continually(0 until n).flatten
    val s2 = Stream.iterate(List.fill(n)(0)) { n => n map (_ + 1) }.flatten
    val s = s1 zip s2
    println("(12a)" + take30(s1))
    println("(12b)" + take30(s2))
    println("(12c)" + take30(s))
    println("(12d)" + s(10))
    println("(12e)" + s(1000000))
    // Much faster than the one before
    println("(12f)" + s(999999))
  }

  def take10[A](s: Iterable[A]) = s.take(10).toList
  def take30[A](s: Iterable[A]) = s.take(30).toList

}

Re: [scala-vienna] Playing with streams Alex 8/26/12 5:51 AM
Thanks for sharing! I learned a lot about the methods available for creating streams.

>  {
>    // How can that be written in one line ?
>    val s1: Stream[Option[String]] = Stream.continually(Some("A"))
>    val s = None #:: s1
>    println("(6)" + take10(s))
>  }

This puzzled me! Short answer to the questions:

val s = Stream.cons(None, Stream.continually(Some("A")))

O.k. But why does the version with #:: not work? Let's look at the compiler message:

scala> val s = None #:: Stream.continually(Some("A"))
<console>:7: error: type mismatch;
 found   : None.type (with underlying type object None)
 required: Some[java.lang.String]
       val s = None #:: Stream.continually(Some("A"))
                    ^

Hmmmm. The compiler wants a object of type Some[String] and does not accept None. Following code is fine for the compiler:

scala> val s = Some("B") #:: Stream.continually(Some("A"))
s: scala.collection.immutable.Stream[Some[java.lang.String]] = Stream(Some(B), ?)

scala> val s = None #:: Stream.continually(None)
s: scala.collection.immutable.Stream[None.type] = Stream(None, ?)

What is going on? If a method is used in operator notation and the method name ends in a colon, the method is invoked on the right operand, i.e. in we have

scala> val s = Stream.continually(Some("A")).#::(None)
<console>:7: error: type mismatch;
 found   : None.type (with underlying type object None)
 required: Some[java.lang.String]
       val s = Stream.continually(Some("A")).#::(None)
                                                 ^

Of course it gives the same compiler message as before. So where is #:: defined?

It is defined in ConsWrapper in Stream.scala with an implicit conversion:

  /** A wrapper class that adds `#::` for cons and `#:::` for concat as operations
   *  to streams.
   */
  class ConsWrapper[A](tl: => Stream[A]) {
    def #::(hd: A): Stream[A] = cons(hd, tl)
    def #:::(prefix: Stream[A]): Stream[A] = prefix append tl
  }

  /** A wrapper method that adds `#::` for cons and `#::: for concat as operations
   *  to streams.
   */
  implicit def consWrapper[A](stream: => Stream[A]): ConsWrapper[A] =
    new ConsWrapper[A](stream)

I.e. we have

scala> val s = new scala.collection.immutable.Stream.ConsWrapper(Stream.continually(Some("A"))).#::(None)
<console>:7: error: type mismatch;
 found   : None.type (with underlying type object None)
 required: Some[java.lang.String]
       val s = new scala.collection.immutable.Stream.ConsWrapper(Stream.continually(Some("A"))).#::(None)
                                                                                                    ^

and are getting the same compiler message again. ConsWrapper is very strict about the types. They have to match. But we can specify the type when creating ConsWrapper:

scala> val s = new scala.collection.immutable.Stream.ConsWrapper[Option[String]](Stream.continually(Some("A"))).#::(None)
s: scala.collection.immutable.Stream[Option[String]] = Stream(None, ?)

:-) That works. Unwinding again using the implicit conversion and specifying the type parameter:

scala> val s = Stream.continually[Option[String]](Some("A")).#::(None)
s: scala.collection.immutable.Stream[Option[String]] = Stream(None, ?)

:-) Now to operator notation:

scala> val s = None #:: Stream.continually[Option[String]](Some("A"))
s: scala.collection.immutable.Stream[Option[String]] = Stream(None, ?)

:-) Now we have an alternative one line notation but it looks more complicated than the first solution Stream.cons(None, Stream.continually(Some("A")))

So what is the signature of Stream.cons?

  /** An alternative way of building and matching Streams using Stream.cons(hd, tl).
   */
  object cons {

    /** A stream consisting of a given first element and remaining elements
     *  @param hd   The first element of the result stream
     *  @param tl   The remaining elements of the result stream
     */
    def apply[A](hd: A, tl: => Stream[A]) = new Cons(hd, tl)

    /** Maps a stream to its head and tail */
    def unapply[A](xs: Stream[A]): Option[(A, Stream[A])] = #::.unapply(xs)
  }

Because we now have one method call with two parameters the compiler can figure out the supertype Option of None and Some  and it works.

But why does it work for lists:

scala> val l = None :: List(Some("A"))
l: List[Option[java.lang.String]] = List(None, Some(A))

That I will explore another time.

Alex