Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I implement a generic mathematical function in Scala

I'm just getting started with Scala and something which I think should be easy is hard to figure out. I am trying to implement the following function:

def square(x:Int):Int = { x * x }

This works just fine, but if I want to try to make this function work for any kind of number I would like to be able to do the following:

def square[T <: Number](x : T):T = { x * x }

This complains and says: error: value * is not a member of type parameter T

Do I need to implement a trait for this?

like image 754
jimmyb Avatar asked Oct 29 '10 22:10

jimmyb


People also ask

How do I use generic in Scala?

To use a generic class, put the type in the square brackets in place of A . Class Apple and Banana both extend Fruit so we can push instances apple and banana onto the stack of Fruit . Note: subtyping of generic types is *invariant*.

What is generic function in Scala?

In Scala, forming a Generic Class is extremely analogous to the forming of generic classes in Java. The classes that takes a type just like a parameter are known to be Generic Classes in Scala. This classes takes a type like a parameter inside the square brackets i.e, [ ].

Does Scala support generics?

Most Scala generic classes are collections, such as the immutable List, Queue, Set, Map, or their mutable equivalents, and Stack. Collections are containers of zero or more objects. We also have generic containers that aren't so obvious at first.


2 Answers

That was one of my first questions in Stack Overflow or about Scala. The problem is that Scala maintains compatibility with Java, and that means its basic numeric types are equivalent to Java's primitives.

The problem arises in that Java primitives are not classes, and, therefore, do not have a class hierarchy which would allow a "numeric" supertype.

To put it more plainly, Java, and, therefore, Scala, does not see any common grounds between a Double's + and a an Int's +.

The way Scala finally got around this restriction was by using Numeric, and its subclasses Fractional and Integral, in the so-called typeclass pattern. Basically, you use it like this:

def square[T](x: T)(implicit num: Numeric[T]): T = {     import num._     x * x } 

Or, if you do not need any of the numeric operations but the methods you call do, you can use the context bound syntax for type declaration:

def numberAndSquare[T : Numeric](x: T) = x -> square(x) 

For more information, see the answers in my own question.

like image 197
Daniel C. Sobral Avatar answered Sep 24 '22 15:09

Daniel C. Sobral


You can define square as:

def square[T: Numeric](x: T): T = implicitly[Numeric[T]].times(x,x) 

This approach has the advantage that it will work for any type T that has an implicit conversion to Numeric[T] (i.e. Int, Float, Double, Char, BigInt, ..., or any type for which you supply an implicit conversion).

Edit: Unfortunately, you'll run into trouble if you try something like List(1,2,3).map(square) (specifically, you'll get a compile error like "could not find implicit value for evidence parameter of type Numeric[T]". To avoid this issue, you can overload square to return a function:

object MyMath {    def square[T: Numeric](x: T) = implicitly[Numeric[T]].times(x,x)    def square[T: Numeric]: T => T = square(_) } 

Hopefully someone with a better understanding of the type inferencer will explain why that is.

Alternatively, one can call List(1,2,3).map(square(_)), as Derek Williams pointed out in the scala-user mailing list thread.

like image 31
Aaron Novstrup Avatar answered Sep 25 '22 15:09

Aaron Novstrup