被“Scala in Depth”选项示例混淆

时间:2012-03-19 20:13:32

标签: java scala option type-inference

Josh Sueresh撰写的新曼宁着作“Scala in Depth”的第2章here。在阅读这篇文章时,我遇到了一些代码:

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")))

}

解释上述代码的后续文本:

  

getTemporaryDirectory方法将命令行参数作为   包含String的Option并返回引用的File对象   我们应该使用的临时目录。我们做的第一件事就是使用   如果有的话,选项上的map方法创建一个java.io.File   参数。接下来,我们确保这个新构造的文件对象   是一个目录。为此,我们使用过滤方法。这将检查   Option中的值是否遵循某些谓词,如果不是,   转换为无。最后,我们检查一下我们是否有值   选项;否则,我们返回默认的临时目录。

所以,对于我来自Java和学习Scala,代码语法让我很困惑。我不知道map(...)函数调用后面有一个点。似乎有很多类型推断发生,我在某处遗漏某些东西而没有看到类型。

对我来说,学习Scala,能够以某种方式查看所有推断的类型,以取消(或取消应用)所有减少,即看起来像Java 6之前的过于冗长的版本,这将非常有用。对于集合类,必须在equals的两侧显示类型。

是否有任何工具可以使用Scala代码片段并显示不同的东西(可能作为标志;一个用于类型,另一个用于隐含,另一个用于括号,另一个用于分号)。我只需要一些东西让我从完全简洁的代码转到更接近Java的东西,这样我就可以自信地在阅读(并最终编写)更简洁的Scala时掌握我的技能。

这就是我要找的东西:

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")));

}

我并没有特别坚持上述内容。我只是无法跟踪链接函数调用如何根据类型推断实际发生的事情。对此的任何帮助将不胜感激。

4 个答案:

答案 0 :(得分:11)

嗯,你确实让REPL逐个尝试链式命令并检查它们的结果类型,但我不确定签名会对你有多大帮助:

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

现在让我们再试一次,从无。

开始
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

所以使用Option帮助我们“传播”None而不检查每一步的空值,就像我们在java中可能做的那样。

如你所见,这里没有发生很多类型推断。 我认为你的困惑的根源可能是mapfilter(以及其他)通常与某种类型的集合相关联,因此可能很难理解他们对Option的做法,这只是远程类似于集合。 为此,我建议您使用经典的scala.Option cheat sheet

答案 1 :(得分:2)

尾随点只是链接到下一行的方法filter。在游戏中没有推论。

这可以重写为

import java.io._

def getTemporaryDirectory(tmpArg : Option[String]) : File = {
    tmpArg.map(name => new File(name)).filter(_.isDirectory).getOrElse(new File(System.getProperty("java.io.tmpdir")))
}

答案 2 :(得分:1)

明确:

def getTemporaryDirectory(tmpArg : Option[String]) : java.io.File = {
  val v1: Option[java.io.File] = tmpArg.map(name => new java.io.File(name))
  val v2: Option[java.io.File] = v1.filter(_.isDirectory)
  val v3: File = v2.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir")))
}

这里还有很多类型推断,Daniel Spiewak有an excellent presentation on type inference。它确实让您了解Scala能够推断出哪些类型。

更具体地说,这个例子。

Option[A]的{​​{1}}方法具有签名map。由于map[B](f: A => B): Option[B]tmpArg,编译器知道参数的类型是Option[String]。现在它可以推断String => Bname。检查函数,可以看到函数返回String。现在,编译器可以推断出参数的类型为File,并且此String => File调用返回map

Option[File]的{​​{1}}方法具有签名Option[A]。函数文字filter只是filter (p: A => Boolean): Option[A]的简写。鉴于_.isDirectory现在是x => x.isDirectory,编译器可以推断出A也是File。结果是_

最后,我们使用File Option[File]方法,签名为Option[A]。语法getOrElse指定类型参数getOrElse[B >: A](default: => B): B被约束为与B >: A相同或超类型。语法B将参数指定为名称/延迟参数,仅在需要时进行评估。传入的参数类型为A,表示=> BFile,此B返回File

答案 3 :(得分:1)

另一个资源以及REPL是使用IDE来分解和/或注释链接表达式。

例如在IntelliJ中我可以将鼠标悬停在方法调用上并查看它的类型签名,如果我有源可用,我可以点击查看实现。

然而,重述丹尼尔的建议,像Scala中的Programming这样的书将是一个更容易的起点,涵盖类型推断以及语言的语法规则(这可能更多地造成了这个例子中的混淆)。