Search code examples
scalascala-streams

Scala worksheet not working for this code , no compilation error shown


I have am trying something in scala worksheet in eclipse. This is not showing any output , and doesn't show any error or warning either.

object stream {
  println("Welcome to the Scala worksheet")

  def cons[T](hd: T, t1: => Stream[T]): Stream[T] = new Stream[T] {
    def head = hd
    private var t1Opt: Option[Stream[T]] = None
    def tail: Stream[T] = t1Opt match {
      case Some(x) => x
      case None => t1Opt = Some(t1); tail
    }
  }

  def streamIncrementedby2(x: Int): Stream[Int] = x #:: streamIncrementedby2(x + 2)

  val x = this.cons(-1, this.streamIncrementedby2(5))
  println(x.head)

}

I am trying out the example in the courera odersky course: scala functional design week3 video. Interestingly in the above example, if I remove everything below the first println statement, I see an evaluated output.

******* Update ******** To help other readers , I am posting corrected version of the above program , inspired by the answer.

 def cons[T](hd: T, t1: => Stream[T]) = new Stream[T] {
    override def head = hd
    override def isEmpty = false
    private[ this ] var t1Opt: Option[Stream[T]] = None
    def tailDefined: Boolean = true
    override def tail: Stream[T] = t1Opt match {
      case Some(x) => x
      case None => {t1Opt = Some(t1); tail}
    }
  } 

Solution

  • If you just want to make a generic element head of a Stream, you can use existing method in Stream package called cons

    def streamIncrementedby2( x: Int ): Stream[ Int ] = x #:: streamIncrementedby2( x + 2 )
    
    val x = Stream.cons( -1, this.streamIncrementedby2( 5 ) )
    println( x.head )
    

    It works fine. However, if you want to make your own version you have to dig deeper. By using the following function definition you are making the constructor, not the ordinary function

    def cons[T](hd: T, t1: => Stream[T]): Stream[T] = new Stream[T] { ...
    

    Key thing here is = new Stream[T]. Therefore you have to provide a lot of stuff that a proper constructor needs: overriding abstract functions like head, tail and isEmpty and providing necessary function tailDefined.

    def classCons[ T ]( hd: T, t1: => Stream[ T ] ): Stream[ T ] = new Stream[ T ] {
    
        override def isEmpty = false
        override def head = hd
        private[ this ] var tlVal: Stream[ T ] = _
    
        def tailDefined: Boolean = tlVal ne null
    
        override def tail: Stream[T] = {
    
            if ( !tailDefined )
                synchronized {
                    if ( !tailDefined ) 
                        tlVal = t1
                }
    
            tlVal
        }
    }
    

    inspiration

    You can also make your cons function a normal function ang get the same result without messing around constructors.

    def funcCons[ T ]( hd: T, t1: => Stream[ T ] ): Stream[ T ] =  {
    
        if ( t1.isEmpty )
            Stream( hd )
        else 
            hd #:: t1.tail
    }
    

    results are the same:

    val ccStream = classCons( -1, this.streamIncrementedby2( 5 ) )
    val ffStream = funcCons( -1, this.streamIncrementedby2( 5 ) )
    println( ccStream.head )
    println( ffStream.head )
    // Result => -1
    // Result => -1