rosicky
rosicky

Reputation: 21

Scala closure always loses value

I am trying to use Scala to write time utilities. It is new to me, especially closures. When I use closures with free variable usingStack to record the calculation memory, I've found that usingStack always is null. When I use Scala IDE debug tools to track the free variable usingStack, the expression view give me the message below:

The type scala.Predef$$less$colon$less cannot be resolved. It is indirectly referenced from required .class files

Attached is the code and the test case without dependency to other classes. You can run it directly.

/**
 *
 */
import java.text.SimpleDateFormat
import java.util.Date

/**
 * {start, end} represent a duration of time.
 * @author Rosicky
 *
 */
trait TimeInterval {
    val df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    def getStart: Option[Date];
    def getEnd: Option[Date];
    /**
     * minus = {x:Date|this.contains(x) && !that.contain(x)}
     */
    def -(that: TimeInterval): TimeInterval;
    /**
     * plus =  {x:Date|this.contains(x) && that.contain(x)}
     */
    def +(that: TimeInterval): TimeInterval;
    /**
     * this exist x
     */
    def contains(x: Date): Boolean;
    /**
     * that forAll this.contains(_)
     */
    def contains(that: TimeInterval): Boolean;
    /**
     * that exist that.contains(_)
     */
    def cross(that: TimeInterval): Boolean;
}
/**
 * zero
 */
case class TimeIntervalZero() extends TimeInterval {
    override def toString = "TimeIntervalZero"
    def getStart: Option[Date] = None
    def getEnd: Option[Date] = None
    def -(that: TimeInterval): TimeInterval = this
    def +(that: TimeInterval): TimeInterval = that match {
        case TimeIntervalUnit(s, e) => new TimeIntervalUnit(s, e)
        case TimeIntervalList(l) => new TimeIntervalList(l)
        case TimeIntervalZero() => this
    }
    def contains(x: Date): Boolean = false
    def contains(that: TimeInterval): Boolean = false
    def cross(that: TimeInterval): Boolean = false
}

/**
 * index given timeunit list by time dimension
 * @author rosicky
 *
 */
private class SortedIndexTimeInterval() {
    import scala.collection.mutable.Map
    private val indexedTimeIntervals: Map[Date, TimeIntervalUnit] = Map()
    private def put(unit: TimeIntervalUnit) {
        indexedTimeIntervals.put(unit.start, unit)
        indexedTimeIntervals.put(unit.end, unit)
    }
    private def sortedIndex: List[Date] = indexedTimeIntervals.keySet.toList.sortWith(_ before _)
    /**
     * looping units by the order of time demension
     * @param matchStart when start founded, execute sth
     * @param matchEnd when end founded, execute sth
     */
    def loopIt(matchStart: ((Date, TimeIntervalUnit) => Unit), matchEnd: ((Date, TimeIntervalUnit) => Unit)) {
        val sI = sortedIndex
        for (d: Date <- sI) {
            val interval: TimeIntervalUnit = indexedTimeIntervals(d)
            d match {
                case interval.start => matchStart(d, interval)
                case interval.end => matchEnd(d, interval)
            }
        }
    }
    /**
     * put units in list to index
     * @param list
     */
    def addList(list: TimeIntervalList) {
        for (unit <- list.l) {
            put(unit)
        }
    }
}
/**
 * unit
 */
case class TimeIntervalUnit(start: Date, end: Date) extends TimeInterval {
    start.before(end) || start.after(end) //constaint
    override def toString = df.format(start) + "-" + df.format(end)
    def before(that: TimeIntervalUnit) = end.before(that.start) && end.before(that.end)
    def after(that: TimeIntervalUnit) = start.after(that.end) && start.after(that.start)
    def getStart = Some(start);
    def getEnd = Some(end);
    def -(that: TimeInterval): TimeInterval = {
        if (!cross(that)) {
            new TimeIntervalUnit(start, end);
        } else if (that.contains(this)) {
            new TimeIntervalZero();
        } else {
            that match {
                case TimeIntervalZero() => new TimeIntervalUnit(start, end)
                case TimeIntervalUnit(s, e) => minusUnit(s, e)
                case TimeIntervalList(l) => minusList(l)
            }
        }
    }

    private def minusList(l: List[TimeIntervalUnit]): TimeInterval = {
        import scala.collection.mutable.Map
        //maping start,end with intervalUnits & this
        val sortedL = l.sortWith(_ before _)
        val thisCopy: TimeInterval = new TimeIntervalList(l)
        val what: Map[Date, TimeIntervalUnit] = Map()
        what.put(start, this)
        what.put(end, this)
        for (u <- sortedL) {
            if (u.cross(thisCopy)) {
                if (u.start.after(start)) what.put(u.start, u)
                if (u.end.before(end)) what.put(u.end, u)
            }
        }
        //sorted all date point
        val sortedDates = what.keySet.toList.sortWith(_ before _).filterNot(_ == end) //not effective
        var u_start: Date = sortedDates.head
        var expectend = true
        var l1: List[TimeIntervalUnit] = List();
        //split minused intervals
        for (d: Date <- sortedDates.tail) { //? does it really has a tail?
            val interval: TimeIntervalUnit = what(d)
            d match {
                case interval.start => l1 = new TimeIntervalUnit(u_start, d) :: l1; expectend = false
                case interval.end => u_start = d; expectend = true
            }
        }
        if (expectend) {
            l1 = new TimeIntervalUnit(u_start, end) :: l1
        }
        new TimeIntervalList(l1.sortWith(_ before _))
    }

    private def minusUnit(s: Date, e: Date): TimeInterval = {
        minusList(List(TimeIntervalUnit(s, e)))
    }

    def +(that: TimeInterval): TimeInterval = that match {
        case TimeIntervalZero() => new TimeIntervalUnit(start, end)
        case TimeIntervalUnit(s, e) => {
            def min(a: Date, b: Date) = if (a.before(b)) a else b
            def max(a: Date, b: Date) = if (a.after(b)) a else b
            if (this.cross(that)) {
                new TimeIntervalUnit(min(start, s), max(end, e))
            } else {
                if (this.after(new TimeIntervalUnit(s, e))) {
                    val l: List[TimeIntervalUnit] = List(new TimeIntervalUnit(s, e), new TimeIntervalUnit(start, end))
                    new TimeIntervalList(l)
                } else {
                    val l: List[TimeIntervalUnit] = List(new TimeIntervalUnit(start, end), new TimeIntervalUnit(s, e))
                    new TimeIntervalList(l)
                }
            }
        }
        case TimeIntervalList(l) => {
            var sum: TimeInterval = new TimeIntervalZero()
            l.foreach(x => { val temp = x + this; sum += temp })
            sum
        }
    }
    def contains(x: Date) = (x.after(start) || x.equals(start)) && (x.before(end) || x.equals(end))
    def contains(that: TimeInterval): Boolean = that match {
        case TimeIntervalZero() => false
        case TimeIntervalUnit(s, e) => (start.before(s) || start.equals(s)) && (end.after(e) || end.equals(e))
        case TimeIntervalList(l) => l forall (this.contains(_))
    }
    def cross(that: TimeInterval): Boolean = that match {
        case TimeIntervalZero() => false
        case TimeIntervalUnit(s, e) => !(s.before(start) && e.before(start)) && !(s.after(end) && e.after(end))
        case TimeIntervalList(l) => l exists (this.cross(_))
    }

}
/**
 * section
 */
case class TimeIntervalList(l: List[TimeIntervalUnit]) extends TimeInterval {
    //TODO l forall !l.cross(each other) && l instanceof TimeIntervalUnit//constraint
    override def toString = l.mkString(",")
    def minElem: TimeIntervalUnit = { var min: TimeIntervalUnit = l.head; l foreach (x => if (x.before(min)) min = x); min }
    def maxElem: TimeIntervalUnit = { var max: TimeIntervalUnit = l.head; l foreach (x => if (x.after(max)) max = x); max }
    override def getStart = Some(minElem.start)
    override def getEnd = Some(maxElem.end)
    def -(that: TimeInterval) = { var sum: TimeInterval = new TimeIntervalZero(); l foreach (x => sum = sum + (x - that)); sum }
    def +(that: TimeInterval) = that match {
        case z: TimeIntervalZero => new TimeIntervalList(l)
        case t: TimeIntervalUnit => plusList(new TimeIntervalList(List(t)))
        case tl: TimeIntervalList => plusList(tl)
    }
    private def plusList(that: TimeIntervalList): TimeInterval = {
        val buffer = new SortedIndexTimeInterval()
        buffer.addList(this)
        buffer.addList(that)
        import scala.collection.mutable.ListBuffer
        var usingStack = new ListBuffer[TimeIntervalUnit]()
        var startD: Date = new Date()
        var retList: List[TimeIntervalUnit] = Nil
        def matchStart(start: Date, unit: TimeIntervalUnit) {
            if (usingStack.isEmpty) {
                startD = start
            }
            usingStack += unit
        }
        def matchEnd(end: Date, unit: TimeIntervalUnit) {
            if (usingStack.isEmpty) {
                retList = new TimeIntervalUnit(startD, end) :: retList
            }
            usingStack -= unit
        }
        buffer.loopIt(matchStart, matchEnd)
        new TimeIntervalList(retList)
    }
    def contains(x: Date) = l exists (_.contains(x))
    def contains(that: TimeInterval) = l exists (_.contains(that))
    def cross(that: TimeInterval) = l exists (_.cross(that))
}

And test case is below:

import org.junit._
import org.scalatest.junit.AssertionsForJUnit
import Assert._
import java.text.SimpleDateFormat
import org.junit.runner.RunWith
class TestTimeInterval extends AssertionsForJUnit {
    @Test def test(): Unit = {
        //zero plus unit
        val df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
        def nU(s1: String, s2: String) = new TimeIntervalUnit(df.parse(s1), df.parse(s2))
        def nL(l: List[TimeIntervalUnit]) = new TimeIntervalList(l);
        val zero = new TimeIntervalZero();
        val october = nU("2011-10-01 00:00:00", "2011-10-31 23:59:59");
        assertEquals(october, zero + october);
        //1 unit minus unit 
        val midOctober = nU("2011-10-10 00:00:00", "2011-10-20 23:59:59");
        assertEquals(
            nL(List(
                nU("2011-10-01 00:00:00", "2011-10-10 00:00:00"),
                nU("2011-10-20 23:59:59", "2011-10-31 23:59:59"))),
            october - midOctober);
        //2 unit minus unit
        val september = nU("2011-09-01 00:00:00", "2011-09-30 23:59:59");
        assertEquals(october, october - september);
        //1 unit minus that
        val someDayOctober = nL(List(
            nU("2011-10-03 00:00:00", "2011-10-03 23:59:59"),
            nU("2011-10-07 00:00:00", "2011-10-07 23:59:59"),
            nU("2011-10-21 00:00:00", "2011-10-21 23:59:59")))
        assertEquals(
            nL(List(
                nU("2011-10-01 00:00:00", "2011-10-03 00:00:00"),
                nU("2011-10-03 23:59:59", "2011-10-07 00:00:00"),
                nU("2011-10-07 23:59:59", "2011-10-21 00:00:00"),
                nU("2011-10-21 23:59:59", "2011-10-31 23:59:59"))),
            october - someDayOctober)
        //1 that minus that
        val threeFirstFiveInOctober = nL(List(
          nU("2011-10-01 00:00:00","2011-10-05 23:59:59"),
          nU("2011-10-10 00:00:00","2011-10-15 23:59:59"),
          nU("2011-10-20 00:00:00","2011-10-25 23:59:59")
        ))
        val threeOtherIntervalInOctober = nL(List(
          nU("2011-10-03 00:00:00","2011-10-07 23:59:59"),
          nU("2011-10-11 00:00:00","2011-10-11 23:59:59"),
          nU("2011-10-13 00:00:00","2011-10-13 23:59:59")
        ))
        assertEquals(nL(List(
            nU("2011-10-01 00:00:00","2011-10-07 23:59:59"),
            nU("2011-10-10 00:00:00","2011-10-15 23:59:59"),
            nU("2011-10-20 00:00:00","2011-10-25 23:59:59"))),
            threeFirstFiveInOctober + threeOtherIntervalInOctober)
       /* assertEquals(nL(List(
          nU("2011-10-01 00:00:00","2011-10-03 00:00:00"),
          nU("2011-10-10 00:00:00","2011-10-11 00:00:00"),
          nU("2011-10-11 23:59:59","2011-10-13 00:00:00"),
          nU("2011-10-13 23:59:59","2011-10-15 23:59:59"),
          nU("2011-10-20 00:00:00","2011-10-25 23:59:59")
        )),threeFirstFiveInOctober - threeOtherIntervalInOctober)*/
    }
}

Upvotes: 0

Views: 1320

Answers (1)

Mirco Dotta
Mirco Dotta

Reputation: 1300

The integration between the Scala IDE v2.0.0 and the Java debugger is limited to a few features and it does not currently support "watching" variables.

What you can do is setting breakpoints in your source code and step through them (using the usual "Step Into", "Step Over" and "Step Return" buttons, available in the Eclipse Debugger perspective).

We plan to improve the debugger for the next 2.1 release of the Scala IDE (which will happen in a while - we just released 2.0 ;)). Though, if you want to stay infomed about new features that will be available in the next 2.1 release, you can follow the scala-ide-user mailing list, and also check the 2.1 nightly.

Upvotes: 1

Related Questions