Search code examples
scalascala-option

Scala: How to return a Some or Option


I have the following piece of code that I am trying to enhance: I am using the java.nio.file package to represent a directory or a file as a Path. So here goes:

import java.nio.file.{Paths,DirectoryStream,Files,
                      Path,DirectoryIteratorException}

val path: Path = Paths.get(directoryPath) 
var directoryStream: Option[DirectoryStream[Path]] = None

// so far so good

try {
    directoryStream = Some(Files.newDirectoryStream(pathO))

    // this is where i get into trouble
    def getMeDirStream: DirectoryStream[Path] =
        if (!directoryStream.isEmpty && directoryStream.isDefined)
            getMeDirStream.get
        else
            None

    // invoke the iterator() method of dstream here
}

The above piece of code will not compile because I do not know what to return in the else, and right now, for the life of me, I can only come up with None, which the compiler simply does not like and I would like to learn what should be its replacement. I want this example to be a learning lesson of Option and Some for me.


Okay, this is where I choke. I would like to check if the directoryStream is not empty and is defined, and then if this is the case, I would like to invoke getMeDirStream.get to invoke the iterator() method on it. The API for Option tells me that invoking the get() method could result in a java.util.NoSuchElementException if the option is empty.

If the directoryStream is empty I want to return something and not None, because IntelliJ is telling me that "Expression of type None.type doesn't conform to expected type DirectoryStream[Path]".

Now, I am being all naive about this.

I would like to know the following:

  1. What should I return in the else other than None?

  2. Should I wrap the getMeDirStream.get in a try-catch with a java.util.NoSuchElementException, even though I am checking if the directoryStream is empty or not.?

  3. What is the purpose of a try-catch in the getMeDirStream.get, if there is indeed such a need?

  4. How can I clean up the above piece of code to incorporate correct checks for being isDefined and for catching appropriate exceptions?

Once I know what to return in the else (and after putting in the appropriate try-catch block if necessary), I would like to invoke the iterator() method on getMeDirStream to do some downstream operations.


Solution

  • Your specific questions are difficult to address because it's unclear exactly what you're trying to achieve. In particular, when you ask what the purpose of the try block is... Well, you wrote it, so only you can answer that.

    In general, you never call get on an Option. You either use pattern matching:

    option match {
        case Some(value) => /* ... */
        case None =>        /* ... */
    }
    

    or you use methods like map, flatMap, and foreach (or the equivalent comprehension syntax that gpampara's code uses).


    My revision of gpampara's answer:

    import scala.collection.convert.wrapAll._
    import scala.util.Try
    import java.nio.file.{Paths, Files, Path}
    
    val getMeDirStream: Option[Iterator[Path]] =
      for {
        path <- Try(Paths.get("")).toOption
        directoryStream <- Try(Files.newDirectoryStream(path)).toOption
      } yield directoryStream.iterator
    

    Changes:

    • Using Try(...).toOption instead of Either
    • Using implicits in scala.collection.convert to return the result as a Scala Iterator.

    Try is similar to Option. Instead of Some and None, it has Success and Failure subtypes, and the failure case includes a Throwable, whereas None is just a singleton with no additional information.