为所有适用类型传递函数

时间:2011-01-04 08:33:44

标签: scala

我按照here找到的建议来定义一个名为square的函数,然后尝试将它传递给一个名为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)) 

当调用两次(square,2)时,REPL会发出错误消息:

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

任何?

4 个答案:

答案 0 :(得分:21)

除了Andrew Phillips之外,我不同意这里的所有人。好吧,到目前为止每个人。 :-)问题出在这里:

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

您希望,像Scala的新手经常这样做,Scala的编译器会将两个参数都考虑到twice以推断出正确的类型。但是,Scala并没有这样做 - 它只使用从一个参数列表到下一个参数列表的信息,而不是从一个参数到下一个参数。这意味着参数fa是独立分析的,而不具备知道对方是什么的优势。

这意味着,例如,有效:

twice(square[Int], 2)

现在,如果你将它分解为两个参数列表,那么它也可以工作:

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

所以,基本上,你要做的一切都是正确的,应该可以正常工作,除了之外你想要一个参数的部分来帮助找出另一个参数的类型(就像你写的那样) )。

答案 1 :(得分:4)

这是来自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)”的类型。我想这是有道理的,但如果斯卡拉大师可以评论这一点,我仍然会很高兴......

答案 2 :(得分:1)

另一种解决方案是将方块提升为部分应用的功能:

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

这样,隐式应用于square,如:

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

但同样,类型参数不会被推断。

答案 3 :(得分:1)

你的问题是 square 不是一个函数(即scala.Function1 [T,T] aka(T)=&gt; 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单态函数文字的简洁性。