Playing with streams | wwagner4 | 8/24/12 6:30 AM | object StreamsTryout extends App { // What basic function exist to create a Stream def take10[A](s: Iterable[A]) = s.take(10).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 ?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 |