3

我昨晚正在做一个项目,并且有一些这样的代码:

/* fixes warnings in 2.10 */
import scala.language.implicitConversions

/* had some kind of case class with some members */
case class Wrapper[A](x: A)

/* for convenience's sake */
implicit def aToWrapper[A](x: A) = Wrapper(x)

/* had some kind of Seq */
val xs: List[Wrapper[String]] =  List("hello", "world", "this", "is", "a", "test")

然后我不小心写道:

xs foldLeft("") { (a, x) => a + x }

遗漏了和.之间的内容。xsfoldLeft

有问题的类型有点复杂,它要求我注释 lambda 的参数类型,所以我很快就做了,认为这是我错误的根源。我最终得到了这样的结果:

xs foldLeft("") { (a: String, x: Wrapper[String]) => a + x }

此时我收到错误:

<console>:13: error: type mismatch;
found   : (String, Wrapper[String]) => String
required: Int
           xs foldLeft("") { (a: String, x: Wrapper[String]) => a + x }
                                                            ^

显然修复是xs.foldLeft("") ...,但我一直想知道为什么编译器在这种情况下需要一个 Int 。谁能阐明这是如何解析的?这整天都在唠叨我。

4

2 回答 2

2

当您省略点和括号时,您使用所谓的中缀表示法。它允许您编写a + b而不是a.+(b). 这里的一个重要规则是,仅当调用具有以下形式时才允许这样做object method paramlist(请参阅SLS 6.12.3):

左结合运算符的右手操作数可能由括号括起来的几个参数组成,例如e op (e 1 , ... , e n ). 然后将该表达式解释为e.op(e 1 , ... , e n )

foldLeft不适合这种形式,它使用object method paramlist1 paramlist2. 因此,如果您用运算符表示法编写此代码,编译器会将其视为object.method(paramlist1).paramlist2(如SLS 6.12.2中所述):

后缀运算符可以是任意标识符。后缀操作e op被解释为e.op.

但这里应用了另一条规则:函数应用程序(SLS 6.6)。

应用程序f(e 1 , ... , e m)将该函数应用于f参数表达式 e 1 , ... , e m

[...]

如果f具有某种值类型,则应用程序被视为等效于f.apply(e 1 , ... , e m),即由 定义的应用方法的应用程序f

开始了:

scala> { (a, x) => a + x }
<console>:12: error: missing parameter type
              { (a, x) => a + x }
                 ^
<console>:12: error: missing parameter type
              { (a, x) => a + x }
                    ^

这只是一个缺少类型参数的函数文字。如果我们添加它们,一切都编译得很好:

scala> { (a: String, x: Wrapper[String]) => a + x }
res6: (String, Wrapper[String]) => String = <function2>

编译器只是应用上述关于函数应用的规则:

scala> "" { (a: String, x: Wrapper[String]) => a + x }
<console>:13: error: type mismatch;
 found   : (String, Wrapper[String]) => String
 required: Int
              "" { (a: String, x: Wrapper[String]) => a + x }
                                                   ^

scala> "" apply { (a: String, x: Wrapper[String]) => a + x }
<console>:13: error: type mismatch;
 found   : (String, Wrapper[String]) => String
 required: Int
              "" apply { (a: String, x: Wrapper[String]) => a + x }
                                                         ^

因此,您的代码被解释为

scala> xs foldLeft ("").apply{ (a: String, x: Wrapper[String]) => a + x }
<console>:14: error: type mismatch;
 found   : (String, Wrapper[String]) => String
 required: Int
              xs foldLeft ("").apply{ (a: String, x: Wrapper[String]) => a + x }
                                                                      ^

但是为什么它会应用函数应用程序规则呢?也可以将函数文字用作后缀运算符。要找出我们收到显示的错误消息的原因,我们需要查看SLS Scala Syntax Summary。在那里我们可以看到以下内容:

  InfixExpr         ::=  PrefixExpr
                      |  InfixExpr id [nl] InfixExpr
  PrefixExpr        ::=  [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr 
  SimpleExpr        ::=  ‘new’ (ClassTemplate | TemplateBody)
                      |  BlockExpr
                      |  SimpleExpr1 [‘_’]
  SimpleExpr1       ::=  Literal
                      |  Path
                      |  ‘_’
                      |  ‘(’ [Exprs] ‘)’
                      |  SimpleExpr ‘.’ id 
                      |  SimpleExpr TypeArgs
                      |  SimpleExpr1 ArgumentExprs
                      |  XmlExpr
  Exprs             ::=  Expr {‘,’ Expr}
  ArgumentExprs     ::=  ‘(’ [Exprs] ‘)’
                      |  ‘(’ [Exprs ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’
                      |  [nl] BlockExpr

从上面描述的部分我们知道ArgumentExprs描述函数应用程序同时InfixExpr描述中缀表达式。由于EBNF的规则,最高的规则具有最低的优先级。并且因为前一个规则被后一个规则调用,这意味着函数字面量是在中缀表达式之前应用的,因此会出现错误消息。

于 2013-03-21T21:41:48.707 回答
1

我相信你只能使用二元运算符作为中缀表示法。我认为您也可以使用括号来补救这种情况:(xs foldLeft ("")) { (a: String, x: Wrapper[String]) => a + x }。可能,它将您的原始代码解析为xs.foldLeft("").{ (a: String, x: Wrapper[String]) => a + x }. 看看这个答案:When to use parenthesis in Scala infix notation

于 2013-03-21T20:38:47.213 回答