I'me learning scala for the purpose of scientific programing. I'm trying to write some simple generic code using spire. I've got the following example working:
import spire.algebra._
import spire.math._
import spire.implicits._
object TestSqrt {
def testSqrt[T: Numeric](x: T) = {
sqrt(x)
}
def main(args: Array[String]){
val x_d: Double = 4.0
println(testSqrt(x_d))
val x_i: Int = 4
println(testSqrt(x_i))
}
}
This prints out 2.0 and 2, as expected. The problem is that I can't get the same thing to work with the exp function. The following code does not compile:
import spire.algebra._
import spire.math._
import spire.implicits._
object TestExp {
def testExp[T: Numeric](x: T) = {
exp(x)
}
def main(args: Array[String]){
val x_d: Double = 4.0
println(testExp(x_d))
val x_i: Int = 4
println(testExp(x_i))
}
}
The compiler says:
... could not find implicit value for parameter t: spire.algebra.Trig[T]
[error] exp(x)
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
Am I doing something wrong, is exp not yet supported, or is this a bug? Can anyone provide a working example of using exp with a generic numeric type from spire?
UPDATE:
I can make exp work by using Trig instead of Numeric, like this:
import spire.algebra._
import spire.math._
import spire.implicits._
object TestExp {
def testExp[T: Trig](x: T) = {
exp(x)
}
def main(args: Array[String]){
val x_d: Double = 1.0
println(testExp(x_d))
val x_f: Float = 1.0f
println(testExp(x_f))
// val x_i: Int = 4
// println(testExp(x_i))
}
}
However, it doesn't work with Int, only floating point types. Further, if I use Trig then I can't use sqrt. The following code does not compile:
}
import spire.algebra._
import spire.math._
import spire.implicits._
object TestSqrt {
def testSqrt[T: Trig](x: T) = {
sqrt(x)
}
def main(args: Array[String]){
val x_d: Double = 4.0
println(testSqrt(x_d))
val x_i: Int = 4
println(testSqrt(x_i))
}
}
And gives the error:
... could not find implicit value for evidence parameter of type spire.algebra.Trig[Int]
[error] println(testSqrt(x_i))
[error] ^
[error] two errors found
[error] (compile:compile) Compilation failed
What should I do if I want both exp and sqrt, and is there a way to make exp work with Integral types?
The type classes are defined to have the same type for the operands and the results of the operations. That is to say, when you run
sqrt
on a value of typeA
, the result will also be of typeA
.Now you may argue that
sqrt
on integers yields integers for some specific input values, such as 4 which you use in your example. On the other hand, what shouldsqrt(5)
return? It seems it will return the greatest integer smaller than the actual square root:This is hardly what you want. Interestingly, when you call the
sqrt
function directly, it will return aDouble
:The reason is that when you import
spire.math._
you get an overloadedsqrt
function:This "solves" the problem by giving the first variant priority (as it doesn't require implicit parametes). What happens in your example is that you use a generic signature, therefore the second version of
sqrt
will be called. TheNRoot
argument is indirectly provided because you say you have aNumeric
.So you will have to decide what you are going to do—do you want to fall back to
Double
when calculating the square root, or go with this rather unconventional idea of potentially truncating the result?Now to exponentiation. In
math
, there are again overloaded versions ofexp
, among them:The difference to
sqrt
is that you won't get aTrig[Int]
under any circumstances. There is just no meaningful way.Let's just define a function that can call
sqrt
anexp
based on the type classes:You see you can use more than one context bound in a function declaration. This syntax is equivalent to
For doubles, this function works (given you have imported
spire.implicits._
):But not for integers:
Integers can always be used where doubles are required, however. So you can make it work: