2013-03-21 54 views
3

我是在一個項目昨晚的工作,並有一些像這樣的代碼:爲什麼Scalac類型不匹配期望Int?

/* 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

當你忽略圓點和圓括號時,你會使用所謂的中綴表示法。它允許你寫a + b而不是a.+(b)。這裏有一個重要的原則是,如果調用的形式object method paramlist這是隻允許(見SLS 6.12.3):

The right-hand operand of a left-associative operator may consist of several arguments enclosed in parentheses, e.g. e op (e 1 , ... , e n) . This expression is then interpreted as e.op(e 1 , ... , e n) .

foldLeft不適合這種形式,它使用object method paramlist1 paramlist2。因此,如果你寫這在操作符號編譯器將其視爲object.method(paramlist1).paramlist2(如SLS 6.12.2描述):

A postfix operator can be an arbitrary identifier. The postfix operation e op is interpreted as e.op .

但是這裏應用的另一個規則:(SLS 6.6)功能的應用。

An application f(e 1 , ... , e m) applies the function f to the argument expressions e 1 , ... , e m .

[...]

If f has some value type, the application is taken to be equivalent to f.apply(e 1 , ... , e m) , i.e. the application of an apply method defined by 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的規則,最高規則的優先級最低。而且由於前者的規則由後者調用,這意味着函數文字在中綴表達式之前應用,因此是錯誤消息。

+0

謝謝您的詳細回覆,僅以您的最後一個示例爲理由,但SLS的摘錄添加了更多上下文。 – jroesch 2013-03-25 01:22:27

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

相關問題