Search code examples
javascalatype-inferencescala-option

Confused by "Scala in Depth" Option example


Chapter 2 of the new Manning book, "Scala in Depth" by Josh Sueresh is posted here. In reading the article, I came across this bit of code:

def getTemporaryDirectory(tmpArg : Option[String]) : java.io.File = {

   tmpArg.map(name => new java.io.File(name)).

          filter(_.isDirectory).

       getOrElse(new java.io.File(System.getProperty("java.io.tmpdir")))

}

The follow up text explaining the above code read:

The getTemporaryDirectory method takes the command line parameter as an Option containing a String and returns a File object referencing the temporary directory we should use. The first thing we do is use the map method on Option to create a java.io.File if there was a parameter. Next, we make sure that this newly constructed file object is a directory. To do that, we use the filter method. This will check whether the value in an Option abides by some predicate and, if not, convert to a None. Finally, we check to see if we have a value in the Option; otherwise, we return the default temporary directory.

So, for me coming from Java and learning Scala, the code syntax confuses me. I don't get how there is a dot following the map(...) function call. It appears there is so much type inference occurring, I am missing something somewhere and not seeing the types.

It would be very helpful to me, learning Scala, to be able to somehow see all the inferred types, to uninfer (or unapply) all the reductions, i.e. the over-verbose version that looks something like pre-Java 6 where the types had to be explict on both sides of the equals for collection classes.

Is there a tool anywhere that would take a Scala code snippet and make explicit different things (perhaps as flags; one for types, another for implicits, another for braces, another for semicolons). I just need something to walk me from completely terse code to something closer to Java so I can feel confident building my skill at reading (and eventually writing) more terse Scala.

Here's kind of what I am looking for:

def getTemporaryDirectory(tmpArg : Option[String]) : java.io.File = {

   ContainerType1[Type1] t1 = tmpArg.map(name => new java.io.File(name));
   ContainerType2[Type2] t2 = t1.filter(_.isDirectory);
   return t2.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir")));

}

I am not stuck on the above specifically. I just am unable to follow how the the chained function calls work in terms of what's actually happening due to type inference. Any help on this would be greatly appreciated.


Solution

  • Well, you do have the REPL to try the chained commands one by one and check their result types, but I'm not sure seeing the signatures will help you so much:

    scala> Some("c:\\users\\paolo")
    res0: Some[java.lang.String] = Some(c:\users\paolo)
    
    scala> res0.map(name => new java.io.File(name))
    res1: Option[java.io.File] = Some(c:\users\paolo)
    
    scala> res1.filter(_.isDirectory)
    res2: Option[java.io.File] = Some(c:\users\paolo)
    
    scala> res2.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir")))
    res3: java.io.File = c:\users\paolo
    

    Now let's try it again, starting with None.

    scala> None:Option[String]
    res6: Option[String] = None
    
    scala> res6.map(name => new java.io.File(name))
    res7: Option[java.io.File] = None
    
    scala> res7.filter(_.isDirectory)
    res8: Option[java.io.File] = None
    
    scala> res8.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir")))
    res9: java.io.File = C:\Users\paolo\AppData\Local\Temp
    

    So using Option helped us "propagate" the None without checking for nulls at every step like we would probably do in java.

    As you see there's not a lot of type inference happening here. I think the root of your confusion may be that map and filter (among others) are usually associated to collections of some kind, so it may be hard to grok what they do on Option, which is only remotely similar to a collection. For that I refer you to the classic scala.Option cheat sheet