开发者

Passing functions for all applicable types around

I followed the advice found here to define a function called square, and then tried to pass it to a function called twice. The functions are defined like this:

 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)) 

When calling twice(square, 2), the REPL spits out an error message:

scala> twice(square, 2)
<开发者_高级运维console>:8: error: could not find implicit value for parameter numeric: Numeric[T]
       twice(square, 2)
         ^

Anyone?


I disagree with everyone here except Andrew Phillips. Well, everyone so far. :-) The problem is here:

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

You expect, like newcomers to Scala often do, for Scala's compiler to take into account both parameters to twice to infer the correct types. Scala doesn't do that, though -- it only uses information from one parameter list to the next, but not from one parameter to the next. That mean the parameters f and a are analyzed independently, without having the advantage of knowing what the other is.

That means, for instance, that this works:

twice(square[Int], 2)

Now, if you break it down into two parameter lists, then it also works:

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

So, basically, everything you were trying to do was correct and should work, except for the part that you expected one parameter to help figuring out the type of the other parameter (as you wrote it).


Here's a session from the 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

So evidently the type of "twice(3)" needs to be known before the implicit can be resolved. I guess that makes sense, but I'd still be glad if a Scala guru could comment on this one...


Another solution is to lift square into partially applied function:

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

This way the implicit is applied to square as in:

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

There is even another approach:

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

But again, the type parameter don't get inferred.


Your problem is that square isn't a function (ie. a scala.Function1[T, T] aka (T) => T). Instead it's a type parametrized method with multiple argument lists one of which is implicit ... there's no syntax in Scala to define an exactly equivalent function.

Interestingly, your use of the Numeric type class means that the usual encodings of higher-ranked functions in Scala don't directly apply here, but we can adapt them to this case and get something like this,

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)
}

This gives us a higher-ranked "function" with its type parameter context-bounded to 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")

We can now define twice in terms of 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

The obvious downside of this approach is that you lose out on the conciseness of Scala's monomorphic function literals.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜