21

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)) 
Community
  • 1
  • 1
eptx
  • 801
  • 7
  • 17
  • 8
    Oah... generic math looks so complicated in scala... – fuz Sep 07 '11 at 16:59
  • 2
    @FUZxxl Consequence of Java legacy and interoperability. – Daniel C. Sobral Sep 08 '11 at 01:27
  • 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 Sep 09 '11 at 00:00
  • 2
    @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 Sep 14 '11 at 19:26

4 Answers4

21

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
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
20

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

user unknown
  • 35,537
  • 11
  • 75
  • 121
Dylan
  • 13,645
  • 3
  • 40
  • 67
11

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
}
Kipton Barros
  • 21,002
  • 4
  • 67
  • 80
Didier Dupont
  • 29,398
  • 7
  • 71
  • 90
2

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)))
}
Philippe
  • 9,582
  • 4
  • 39
  • 59