为什么Scala类型不匹配期望Int?

时间:2013-03-21 20:24:53

标签: scala type-inference typechecking scalac

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

/* 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。任何人都能说明这是如何被解析的吗?这一直困扰着我。

2 个答案:

答案 0 :(得分: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的规则,最上面的规则具有最低优先级。并且因为前一个规则被后者调用,这意味着函数文字在中缀表达式之前应用,因此错误消息。

答案 1 :(得分:1)

我相信你只能使用二元运算符作为中缀表示法。我想你也可以通过使用括号来解决这个问题:(xs foldLeft(“”)){(a:String,x:Wrapper [String])=&gt; a + x}。 可能它会将您的原始代码解析为xs.foldLeft("").{ (a: String, x: Wrapper[String]) => a + x }。看看这个答案:When to use parenthesis in Scala infix notation