Что такое признак FunctionN, представляющий функцию, принимающую параметр по имени? - PullRequest
7 голосов
/ 24 ноября 2011

Функция в Scala - это объект, который реализует одну из черт FunctionN. Например:

scala> def f(x: Int) = x * x
f: (x: Int)Int

scala> val ff = f _
ff: Int => Int = <function1>

scala> val fff: Function1[Int, Int] = f _
fff: Int => Int = <function1>

Пока все хорошо. Но что, если у нас есть функция, которая принимает параметр по имени? Это, конечно, все еще реализует одну из черт FunctionN:

scala> def g(x: => Int) = x * x
g: (x: => Int)Int

scala> val gg = g _
gg: => Int => Int = <function1>

scala> gg.isInstanceOf[Function1[_, _]]
res0: Boolean = true

Но какого именно типа? Это не Function1[Int, Int]:

scala> val ggg: Function1[Int, Int] = g _
<console>:8: error: type mismatch;
 found   : => Int => Int
 required: Int => Int
       val ggg: Function1[Int, Int] = g _
                                      ^

И это не Function1[Function0[Int], Int]:

scala> val ggg: Function1[Function0[Int], Int] = g _
<console>:8: error: type mismatch;
 found   : => Int => Int
 required: () => Int => Int
       val ggg: Function1[Function0[Int], Int] = g _
                                                 ^

И Function1[=> Int, Int] не компилируется:

scala> val ggg: Function1[=> Int, Int] = g _
<console>:1: error: identifier expected but '=>' found.
       val ggg: Function1[=> Int, Int] = g _
                          ^

Так что это?

Ответы [ 2 ]

5 голосов
/ 24 ноября 2011

Имя очень полезно, но небезопасно вне системы типов

Параметры имени Scala являются синтаксическим сахаром, чтобы сделать код более читабельным, когда требуется ленивая оценка. Без этого нам нужно было бы поставить "() =>" перед всем, что должно было быть ленивым. Тем не менее, хотя это всего лишь функция 0 во время выполнения, на уровне системы типизации было бы проблематично, если бы вы могли определить что-либо, кроме параметра, имеющего тип по имени. Также помните, что черты FunctionN существуют в основном для реализации и взаимодействия Java, поскольку в Java и JVM не существует такого типа функций, как тип.

Быть явным

Если вам нужно четко указать при наборе текста, следующее позволит вам быть ограничительным

def g(x: => Int) = x * x
val ggg: (=> Int) => Int = g _

Более сложный набор

Типизацию по имени можно использовать только внутри части параметров объявлений типов функций. Сами типы функций могут затем использоваться внутри других параметризованных типов.

var funks: List[(=> Int) => Int] = Nil
funks ::= ggg
funks foreach { _ { println("hi"); 5 } }
1 голос
/ 24 ноября 2011

Ответ Рекса Керра на на этот вопрос дает подсказку: аргумент по имени в конечном итоге преобразуется в Function0, но, вероятно, обрабатывается специально во время компиляции.

Вы можете проверить это:

scala> gg(sys.error("me"))
java.lang.RuntimeException: me
    at scala.sys.package$.error(package.scala:27)
    at $anonfun$1.apply(<console>:10)
    at $anonfun$1.apply(<console>:10)
    at scala.Function0$class.apply$mcI$sp(Function0.scala:34)
    at scala.runtime.AbstractFunction0.apply$mcI$sp
    ...

EDIT

Если продолжить мой первый комментарий, это также означает, что вы не можете указать type для параметра по имени:

def test[A: Manifest](fun: Function1[A, Int]): Unit =
  println("Found " + implicitly[Manifest[A]])

scala> test(gg)
<console>:11: error: No Manifest available for => Int.
              test(gg)
                  ^
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...