Передача функций для всех применимых типов вокруг - PullRequest
6 голосов
/ 04 января 2011

Я последовал совету, найденному здесь , чтобы определить функцию с именем square, а затем попытался передать ее функции, вызываемой дважды.Функции определены следующим образом:

 def square[T](n: T)(implicit numeric: Numeric[T]): T = numeric.times(n, n)
 def twice[T](f: (T) => T, a: T): T = f(f(a)) 

При двойном вызове (квадрат, 2) REPL выдает сообщение об ошибке:

scala> twice(square, 2)
<console>:8: error: could not find implicit value for parameter numeric: Numeric[T]
       twice(square, 2)
         ^

Любой?

Ответы [ 4 ]

21 голосов
/ 04 января 2011

Я не согласен со всеми здесь, кроме Эндрю Филлипс . Ну, пока все. :-) Проблема здесь:

def twice[T](f: (T) => T, a: T): T = f(f(a))

Вы ожидаете, как это часто делают новички в Scala, чтобы компилятор Scala учитывал оба параметра до twice, чтобы вывести правильные типы. Однако Scala этого не делает - она ​​использует информацию только из одного списка параметров в следующий, но не из одного параметра в другой. Это означает, что параметры f и a анализируются независимо друг от друга, не имея возможности знать, что такое другой.

Это означает, например, что это работает:

twice(square[Int], 2)

Теперь, если разбить его на два списка параметров, то это также сработает:

def twice[T](a: T)(f: (T) => T): T = f(f(a))
twice(2)(square)

Итак, в основном все, что вы пытались сделать, было правильным и должно работать, за исключением для части, которую вы ожидали, один параметр поможет выяснить тип другого параметра (как вы его написали) .

4 голосов
/ 04 января 2011

Вот сессия из Scala REPL.

Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def square[T : Numeric](n: T) = implicitly[Numeric[T]].times(n, n)
square: [T](n: T)(implicit evidence$1: Numeric[T])T

scala> def twice2[T](f: T => T)(a: T) = f(f(a))
twice2: [T](f: (T) => T)(a: T)T

scala> twice2(square)(3)
<console>:8: error: could not find implicit value for evidence parameter of type
 Numeric[T]
       twice2(square)(3)
              ^

scala> def twice3[T](a: T, f: T => T) = f(f(a))
twice3: [T](a: T,f: (T) => T)T

scala> twice3(3, square)
<console>:8: error: could not find implicit value for evidence parameter of type
 Numeric[T]
       twice3(3, square)

scala> def twice[T](a: T)(f: T => T) = f(f(a))
twice: [T](a: T)(f: (T) => T)T

scala> twice(3)(square)
res0: Int = 81

Таким образом, очевидно, что тип «дважды (3)» должен быть известен, прежде чем неявное может быть разрешено. Я думаю, это имеет смысл, но я все равно буду рад, если гуру Scala сможет прокомментировать это ...

1 голос
/ 04 января 2011

Ваша проблема в том, что квадрат не является функцией (т. Е. Scala.Function1 [T, T] aka (T) => T).Вместо этого это метод параметризованного типа с несколькими списками аргументов, один из которых неявный ... в Scala нет синтаксиса для определения точно эквивалентной функции.

Интересно, что вы используете тип Numericкласс означает, что обычные кодировки функций более высокого ранга в Scala здесь не применяются напрямую, но мы можем адаптировать их к этому случаю и получить что-то вроде этого:

trait HigherRankedNumericFunction {
  def apply[T : Numeric](t : T) : T
}

val square = new HigherRankedNumericFunction {
  def apply[T : Numeric](t : T) : T = implicitly[Numeric[T]].times(t, t)
}

Это дает нам более высокий рейтинг«функция» со своим параметром типа, привязанным к Numeric,

scala> square(2)
res0: Int = 4

scala> square(2.0)
res1: Double = 4.0

scala> square("foo")
<console>:8: error: could not find implicit value for evidence parameter of type Numeric[java.lang.String]
   square("foo")

Теперь мы можем определить дважды в терминах HigherRankedNumericFunctions,

def twice[T : Numeric](f : HigherRankedNumericFunction, a : T) : T = f(f(a))

scala> twice(square, 2)
res2: Int = 16

scala> twice(square, 2.0)
res3: Double = 16.0

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

1 голос
/ 04 января 2011

Другое решение состоит в том, чтобы поднять квадрат в частично примененную функцию:

scala> twice(square(_:Int),2)
res1: Int = 16

Таким образом, неявное применяется к квадрату, как в:

scala> twice(square(_:Int)(implicitly[Numeric[Int]]),2)
res3: Int = 16

Существует даже другой подход:

def twice[T:Numeric](f: (T) => T, a: T): T = f(f(a))
scala> twice[Int](square,2)
res1: Int = 16

Но опять же, параметр типа не выводится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...