views:

132

answers:

3

Hi

I am Peter Pilgrim. I watched Martin Odersky create a control abstraction in Scala. However I can not yet seem to repeat it inside IntelliJ IDEA 9. Is it the IDE?

package demo

class Control {

  def repeatLoop ( body: => Unit ) = new Until( body )

  class Until( body: => Unit ) {
    def until( cond: => Boolean ) {
      body;
      val value: Boolean = cond;
      println("value="+value)
      if ( value ) repeatLoop(body).until(cond)
      // if  (cond) until(cond)
    }
  }

  def doTest2(): Unit = {
    var y: Int = 1
    println("testing ... repeatUntil() control structure")
    repeatLoop {
      println("found y="+y)
      y = y + 1
    }
    { until ( y < 10 ) }
  }

}

The error message reads:

Information:Compilation completed with 1 error and 0 warnings
Information:1 error
Information:0 warnings
C:\Users\Peter\IdeaProjects\HelloWord\src\demo\Control.scala
Error:Error:line (57)error: Control.this.repeatLoop({
scala.this.Predef.println("found y=".+(y));
y = y.+(1)
}) of type Control.this.Until does not take parameters
repeatLoop {

In the curried function the body can be thought to return an expression (the value of y+1) however the declaration body parameter of repeatUntil clearly says this can be ignored or not?

What does the error mean?

+6  A: 

You don't need the 2nd pair of braces, the usage should be:

repeatLoop (x) until (cond) //or...
repeatLoop {x} until {cond}

And not:

repeatLoop {x} { until(cond) } //EXTRA PAIR OF BRACES

The error means that Scala thinks you are trying to call a method with a signature something like:

def repeatLoop(x: => Unit)(something: X) //2 parameter lists

And can find no such method. It is saying "repeatLoop(body)" does not take parameters. A full code listing for the solution probably looks something a bit more like:

object Control0 {
  def repeatLoop(body: => Unit) = new Until(body)

  class Until(body: => Unit) {
    def until(cond: => Boolean) {
      body;
      val value: Boolean = cond;

      if (value) repeatLoop(body).until(cond)
    }
  }


  def main(args: Array[String]) {
    var y: Int = 1
    println("testing ... repeatUntil() control structure")
    repeatLoop {
      println("found y=" + y)
      y += 1
    }.until(y < 10)
  }
}

There are two useful observations to make here:

  1. The solution is not tail-recursive and will result in a StackOverflowError for long iterations (try while (y < 10000))
  2. The until seems the wrong way round to me (it would be more natural to stop when the condition becomes true, not carry on while it is true).
oxbow_lakes
Ah I see. Thanks very much. The code change does in fact work1. Point taken2. It was Martin's Odersky exercise. Yes you write the control abstraction the other way with the ``whileLoop( body: => Boolean)'' is easier. I think you can make that tail recursive easier too.I was attempt to replicate the "repeat" ... "until" structure, hence I did not want the extra (".") between the "repeatLoop" function type and the "until" funtion object method.If you try to this then there is compilation error: not found value until.
peter_pilgrim
+3  A: 

Here is a solution without the StackOverflowError.

scala>   class ConditionIsTrueException extends RuntimeException
defined class ConditionIsTrueException

scala>   def repeat(body: => Unit) = new {
 |     def until(condition: => Boolean) = { 
 |       try {
 |         while(true) {
 |           body
 |           if (condition) throw new ConditionIsTrueException
 |         }   
 |       } catch {
 |         case e: ConditionIsTrueException =>
 |       }   
 |     
 |     }   
 |   }
repeat: (body: => Unit)java.lang.Object{def until(condition: => Boolean): Unit}

scala> var i = 0              
i: Int = 0

scala> repeat { println(i); i += 1 } until(i == 3)
0
1
2

scala> repeat { i += 1 } until(i == 100000)       

scala> repeat { i += 1 } until(i == 1000000)

scala> repeat { i += 1 } until(i == 10000000)

scala> repeat { i += 1 } until(i == 100000000)

scala> 

According to Jesper and Rex Kerr here is a solution without the Exception.

def repeat(body: => Unit) = new {
  def until(condition: => Boolean) = { 
    do {
      body
    } while (!condition)
  }   
}
michael.kebe
Nice. I like the anonymous function object.
peter_pilgrim
it's called a structural type
oxbow_lakes
Why try/catch when you can `do { body } while (!condition)`?
Rex Kerr
Abusing exceptions for normal control flow, ugly!
Jesper
Thanks for the hint, why I forget that there is a `do-while`? ;)
michael.kebe
What is the type of "new { /*... */ }" ?Is it like an anonymous function object (functor) just like C++?
peter_pilgrim
+5  A: 

How about a one liner for repeat until.

def repeat(b: => Unit) = new AnyRef {def until(c: => Boolean) {b; while (! c) b}}

Which, for example, gives:-

scala> repeat {
     |   println("i = "+i)
     |   i+=1
     | } until (i >= 10)
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
Don Mackenzie