21
votes

Trying to implement, in Scala, the following Haskell function (from Learn You a Haskell...) so that it works with Int, Double, etc.

doubleUs x y = x * 2 + y * 2 

Note that this is similar to Scala: How to define "generic" function parameters?

Here's my attempt and error. Can someone explain what's happening and offer a solution. Thanks.

scala> def doubleUs[A](x:A,y:A)(implicit numeric: Numeric[A]): A = numeric.plus(numeric.times(x,2),numeric.times(y,2)) 
<console>:34: error: type mismatch;
 found   : Int(2)
 required: A
       def doubleUs[A](x:A,y:A)(implicit numeric: Numeric[A]): A = numeric.plus(numeric.times(x,2),numeric.times(y,2)) 
4
Oah... generic math looks so complicated in scala...fuz
@FUZxxl Consequence of Java legacy and interoperability.Daniel C. Sobral
Coming from a C#/F#/Haskell/Mathematica background, I have to say my heart beats with bliss upon such sights of ugliness that plague languages I deliberately chose to keep away from after brief initial exposure, such as Java/Scala ^_^! There is no god in heavens that would not pity the ones that suffer such mess on a daily basis.Cetin Sert
@Cetin: In Scala, generic math is ugly. But it can do generic math. C#, F# cannot even do generic math in a typesafe manner. Mathematica being a dynamically typed language, the comparison doesn't make sense.missingfaktor

4 Answers

21
votes

In addition to what @Dylan said, you can make it look a little less tedious by importing into scope the contents of Numeric implicit as shown below:

scala> def doubleUs[N](x: N, y: N)(implicit ev: Numeric[N]) = {
     |   import ev._
     |   x * fromInt(2) + y * fromInt(2)
     | }
doubleUs: [N](x: N, y: N)(implicit ev: Numeric[N])N

scala> doubleUs(3, 4)
res9: Int = 14

scala> doubleUs(8.9, 1.2)
res10: Double = 20.2
20
votes

You are using the Int literal 2 but scala is expecting the Numeric type A. The Scala Numeric API has a utility function- def fromInt(x:Int): T. This is what you want to use, so replace your usage of 2 with numeric.fromInt(2)

def doubleUs[A](x:A,y:A)(implicit numeric: Numeric[A]): A =
  numeric.plus (numeric.times (x, numeric.fromInt (2)), numeric.times (y, numeric.fromInt (2)))

Also, since a Numeric instance defines an implicit conversion to an Ops, you can import numeric._ and then say x * fromInt(2) + y * fromInt(2).

11
votes

You need some implicits in scope:

def doubleUs[A](x: A, y: A)(implicit num: Numeric[A]) = {
  import num._
  implicit def fromInt(i: Int) = num.fromInt(i)
  x * 2 + y * 2
}
2
votes

Dylan essentially answered, but for what it's worth, let me suggest to use the context bound syntax instead of the implicit argument (both are equivalent, and the former is automatically rewritten into the latter by the compiler).

def doubleUs[A : Numeric](x : A, y : A) : A = {
  val num = implicitly[Numeric[A]]
  import num.{plus,times,fromInt}
  plus(times(x, fromInt(2)), times(y, fromInt(2)))
}