Подтвердить что ты не робот

Как анонимная функция "case" действительно работает в Scala?

Dеar Scala,

scala> val f1: ((Int, Int)) => Int = { case (a, b) => a + b }
f1: ((Int, Int)) => Int = <function1>

scala> val f2: (Int, Int) => Int = { case (a, b) => a + b }
f2: (Int, Int) => Int = <function2>

а?!

scala> f1(1, 2)
res2: Int = 3

Ok...

scala> def takesIntInt2Int(fun: (Int, Int) => Int) = fun(100, 200)
takesIntInt2Int: (fun: (Int, Int) => Int)Int

scala> def takesTuple2Int(fun: ((Int, Int)) => Int) = fun(100, 200)
takesTuple2Int: (fun: ((Int, Int)) => Int)Int

scala> takesIntInt2Int(f2)
res4: Int = 300

scala> takesIntInt2Int(f1)
<console>:10: error: type mismatch;
 found   : ((Int, Int)) => Int
 required: (Int, Int) => Int
              takesIntInt2Int(f1)
                              ^

scala> takesTuple2Int(f1)
res6: Int = 300

scala> takesTuple2Int(f2)
<console>:10: error: type mismatch;
 found   : (Int, Int) => Int
 required: ((Int, Int)) => Int
              takesTuple2Int(f2)

Right. А теперь посмотри на это!

scala> takesTuple2Int { case (a, b, c) => a + b + c }
<console>:9: error: constructor cannot be instantiated to expected type;
 found   : (T1, T2, T3)
 required: (Int, Int)
              takesTuple2Int { case (a, b, c) => a + b + c }
                                    ^

scala> takesIntInt2Int { case (a, b, c) => a + b + c }
<console>:9: error: constructor cannot be instantiated to expected type;
 found   : (T1, T2, T3)
 required: (Int, Int)
              takesIntInt2Int { case (a, b, c) => a + b + c }

Как, srsly? o_O Оба результата приводят к ошибке required: (Int, Int).

Зачем использовать case вообще в таких анонимных функциях?

4b9b3361

Ответ 1

См. раздел 8.5 справочника Scala (http://www.scala-lang.org/files/archive/nightly/pdfs/ScalaReference.pdf). Выражение { case (a, b) => a + b } интерпретируется по-разному на основе ожидаемого типа. В вашем определении f1 он создал PartialFunction[(Int, Int), Int], который был добавлен в Function1[(Int, Int), Int], т.е. ((Int, Int)) => Int, тогда как в определении f2 он создал Function2[Int, Int, Int], т.е. (Int, Int) => Int.

Эти две интерпретации относятся к двум ситуациям, в которых вы обычно используете случай в анонимной функции.

Один из них предназначен для написания анонимных функций, которые принимают кортежи и работают с их компонентами, как и с f1. Примером может служить функция, которую вы передаете методу foreach или map на map, например. Map(1 -> 2, 3 -> 4) map { case (k, v) => k + v }.

Вторая - для написания анонимной функции, которая делает match единственным параметром. Ваш f2 делает это, но не каким-либо полезным способом. Примером может служить анонимная функция, переданная в collect, например. List(1, -2, 3) collect { case x if x > 0 => -x }.

Обратите внимание, что эти два могут быть объединены, то есть функции, подобные f1, могут также выполнять сложное сопоставление. Например, Map(1 -> 2, 3 -> 4) collect { case (k, v) if k < 2 => v }.

Изменить: res2 работает из-за tupling. Если приложение не вводит проверку, компилятор будет пытаться обернуть аргументы в кортеже перед сбоем.

Но это испробовано только для приложений; это не общее преобразование, как вы обнаружили. Он не будет пытаться обновить значение Function2[A, B, C] до Function1[(A, B), C].