Search code examples
scalausingioexceptionfile-read

Scala USING of USING


I have csv with data like this:

1;57840
2;57840
3;57834
...

And simple scala code to read this data into Seq of Tuple2[Int,Int]

Code:

case class Tick(Data : Tuple2[Int,Int]) {
  val tNum : Int = Data._1
  val tVal : Int = Data._2
}

case class TicksDs(Data : Seq[Tick]) {
  def size : Int = Data.size
}

class scvReader(CsvPath : String){

  def using[A <: { def close(): Unit }, TicksDs](param: A)(f: A => TicksDs): TicksDs =
    try {
      f(param)
    } finally {
      //param.close()
    }

  def readTicks : TicksDs = {
    using(io.Source.fromFile(CsvPath)) { source =>
       TicksDs(source.getLines.map{oneLine => (oneLine.split(";")(0).toInt-1,
                                               oneLine.split(";")(1).toInt)}.toSeq.map(x => Tick(x._1,x._2)))
    }
  }

}

object CurryingFuncs extends App {
  val ticks : TicksDs = new scvReader("data/simple.csv").readTicks
  println("ticks.size="+ticks.size+" ticks(Class): "+ticks.getClass.getName)
  for (tick <- ticks.Data) {
    println(tick.tNum+" "+tick.tVal+" "+tick.getClass.getName)
  }
}

It's everything fine and this return me output:

ticks.size=50 ticks(Class): TicksDs
0 57840 Tick
1 57840 Tick
2 57834 Tick
3 57831 Tick
...

But when I uncomment line //param.close() it raise error

Exception in thread "main" java.io.IOException: Stream Closed
    at java.io.FileInputStream.readBytes(Native Method)
...
...

Where is mistake and how fix it? Thanks.

Fix (thanks to jwvh):

    ...
      def using[A <: { def close(): Unit }, B](param: A)(f: A => B): B =
        try f(param)
         finally param.close()
    ...

   ...
        using(io.Source.fromFile(CsvPath)) { source =>
           TicksDs(source.getLines.map{oneLine => (oneLine.split(";")(0).toInt-1,
                                                   oneLine.split(";")(1).toInt)}.toList.map(x => Tick(x._1,x._2))) //was .toSeq now .toList
        }

Solution

  • The error comes from the fact that you're closing the source before it is emptied.

    This, source.getLines, produces an Iterator[String] which is evaluated lazily, and the toSeq later that same line doesn't change it. If you use toList instead then that will force evaluation of the complete file contents before the close().

    Also, you don't need TicksDs as a type parameter to using(). It's not serving any purpose.

    def using[A <: {def close(): Unit}]( . . .