Scala - delete file if exist, the Scala way

Johnny picture Johnny · May 3, 2015 · Viewed 29.1k times · Source

How to nicely delete file in Scala, "the Scala way"?

For example, i can use something like this, very Java style:

  private def deleteFile(path: String) = {
    val fileTemp = new File(path)
    if (fileTemp.exists) {
       fileTemp.delete()
    }
  }

How would it be implemented in Scala, in a more functional syntax?

Answer

dk14 picture dk14 · May 3, 2015

You can't get rid of side effects while doing IO-operations, so no good functional ways here. All functional stuff is actually ends when you start to interact with user/devices directly, no monad can help you to do one external side-effect; however, you can describe (wrap) sequential side-effects using IO-like Monads.

Talking about your example, monad-restyled code may look like:

implicit class FileMonads(f: File) {
  def check = if (f.exists) Some(f) else None //returns "Maybe" monad
  def remove = if (f.delete()) Some(f) else None //returns "Maybe" monad
}

for {
  foundFile <- new File(path).check
  deletedFile <- foundFile.remove
} yield deletedFile

res11: Option[java.io.File] = None

But that's too verbose without any real advantages if you just want to delete one file. More than that, fileTemp.exists check has no sense and actually isn't reliable (as @Eduardo pointed out). So, even in Scala the best way I know is FileUtils.deleteQuietly:

  FileUtils.deleteQuietly(new File(path))

Or even

  new File(path).delete()

It won't throw an exception for non-existent file - just return false.

If you really want something more Scala-way - look at rapture.io for example:

  val file = uri"file:///home/work/garbage"
  file.delete()

Or scala-io. More info: How to do File creation and manipulation in functional style?

P.S. However IO-monads might be useful (unlike Some/None in my case) when you require asynchronous operations, so naive code (without cats/scalaz) would look like:

implicit class FileMonads(f: File) {
  def check = Future{ f.exists } //returns "Future" monad
  def remove = Future{ f.remove } //returns "Future" monad
}

for {
  exists <- new File(path).check
  _ <- if (exists) foundFile.remove else Future.unit
}

Of course, in the real world, it's best to use some NIO-wrappers like FS2-io: https://lunatech.com/blog/WCl5OikAAIrvQCoc/functional-io-with-fs2-streams