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 type A
, the result will also be of type A
.
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 should sqrt(5)
return? It seems it will return the greatest integer smaller than the actual square root:
def testSqrt[T: Numeric](x: T): T = sqrt(x)
scala> testSqrt(5)
res0: Int = 2
This is hardly what you want. Interestingly, when you call the sqrt
function directly, it will return a Double
:
scala> sqrt(5)
res1: Double = 2.23606797749979
The reason is that when you import spire.math._
you get an overloaded sqrt
function:
final def sqrt(x: Double): Double = Math.sqrt(x)
final def sqrt[A](a: A)(implicit ev: NRoot[A]): A = ev.sqrt(a)
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. The NRoot
argument is indirectly provided because you say you have a Numeric
.
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 of exp
, among them:
final def exp(n: Double): Double = Math.exp(n)
final def exp[A](a: A)(implicit t: Trig[A]): A = t.exp(a)
The difference to sqrt
is that you won't get a Trig[Int]
under any circumstances. There is just no meaningful way.
Let's just define a function that can call sqrt
an exp
based on the type classes:
def sqrtAndExp[T: NRoot: Trig](x: T): (T, T) = (sqrt(x), exp(x))
You see you can use more than one context bound in a function declaration. This syntax is equivalent to
def sqrtAndExp[T](x: T)(implicit nroot: NRoot[T], trig: Trig[T]: (T, T) =
(sqrt(x), exp(x))
For doubles, this function works (given you have imported spire.implicits._
):
scala> sqrtAndExp(5.0)
res2: (Double, Double) = (2.23606797749979,148.4131591025766)
But not for integers:
scala> sqrtAndExp(5)
<console>:18: error: could not find implicit value for evidence parameter of type
spire.algebra.Trig[Int]
sqrtAndExp(5)
^
Integers can always be used where doubles are required, however. So you can make it work:
scala> sqrtAndExp[Double](5)
res3: (Double, Double) = (2.23606797749979,148.4131591025766)