2013-03-21 8 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 } 

покинув . между xs и foldLeft.

Типы, о которых идет речь, были немного сложнее, и он попросил меня аннотировать параметры параметров лямбда, поэтому я быстро сделал это, думая, что это стало причиной моей ошибки. Я в конечном итоге с чем-то вроде этого:

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

1

Я считаю, что вы можете использовать только двоичные операторы как указание infix. Я думаю, вы также можете исправить ситуацию, используя скобки: (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

Смежные вопросы