昨晚我正在做一个项目,并且有一些像这样的代码:
/* 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 }
将.
放在xs
和foldLeft
之间。
有问题的类型有点复杂,它要求我注释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。任何人都能说明这是如何被解析的吗?这一直困扰着我。
答案 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