10

I thinks it's easier to explain it with a simple example. (help rephrasing the title is welcome ;-)

I'd like to implement a squared method and, using implicit def, automatically add it to any class that supports the *-operator.

With an Int it's very easy:

class EnhancedInt(x: Int) { def squared = x * x }

implicit def IntToEnchancedInt(x: Int) = new EnhancedInt(x)

But with Any or AnyVal I get the following error:

scala> class EnhanceAny(x: AnyVal) { def squared = x * x }
<console>:7: error: value * is not a member of AnyVal
       class EnhanceAny(x: AnyVal) { def squared = x * x }

I'd like to know how I could apply it to any numeric class, or, even better, to any class supporting the *-operator.

user unknown
  • 35,537
  • 11
  • 75
  • 121
opensas
  • 60,462
  • 79
  • 252
  • 386
  • +1 good question. This is the kind of Scala stuff that I have trouble with. In Haskell, I'd just use `:t \x -> x * x` in ghci (and it tells me `Num a => a -> a`), but in Scala, when I enter `x => x * x` in the REPL, it complains that I haven't given `x` a type. And then I wave my hands around and remind myself that Scala's type system is more "powerful" than Haskell's. – Dan Burton Jan 14 '12 at 23:41
  • It may work with Numeric instead of AnyVal. You should look how Seq.product() manages to get access to multiplication. – scand1sk Jan 15 '12 at 00:26
  • @DanBurton: Perhaps you didn't interpret the question correctly. See Dan Simon's answer below. – missingfaktor Jan 15 '12 at 06:00
  • @DanBurton - why is Scala's type system is more powerful than Haskell's? Can you give me some links to read about it? – Rogach Jan 15 '12 at 06:42
  • @Rogach - for one thing, Scala's type system has subtyping; Haskell's doesn't. Implicit conversion from one type to another (in fact, the whole concept of "implicit") is another Scala feature that Haskell doesn't really have. – Dan Burton Jan 15 '12 at 07:02

3 Answers3

8

It's not possible to have solution that works on any type with a * method without writing a boilerplate conversion for each type you want to deal with. Essentially to do that you would need a recursive structural type, and Scala does not support those because of JVM type erasure. See this post for more details.

You can get fairly close to what you want using a type class along with the Numeric type class, (inspired by the answers to this and this question). This will work with most primitives:

//define the type class
trait Multipliable[X] { def *(x: X): X}

//define an implicit from A <% Numeric[A] -> Multipliable[Numeric[A]]
implicit def Numeric2Mult[A](a: A)(implicit num: Numeric[A]): Multipliable[A] = new Multipliable[A]{def *(b: A) = num.times(a, b)}

//now define your Enhanced class using the type class
class EnhancedMultipliable[T <% Multipliable[T]](x: T){ def squared = x * x}

//lastly define the conversion to the enhanced class
implicit def Mult2EnhancedMult[T <% Multipliable[T]](x: T) = new EnhancedMultipliable[T](x)

3.squared
//Int = 9

3.1415F.squared
//Float = 9.869022

123456789L.squared
//Long = 15241578750190521
Community
  • 1
  • 1
Dan Simon
  • 12,891
  • 3
  • 49
  • 55
  • Grate, I tried it and it works, that's why I marked it as the correct answer... but there are a couple of things I'm missing... Are you overriding the "*" operator for every Numeric class??? – opensas Jan 15 '12 at 15:34
  • 1
    Sort of, it's more just providing the implementation for an interface rather than overriding. The `Multipliable` trait is supposed to represent any type that has some notion of multiplication. For Numeric types, we're basically saying that this multiplication should be the actual "regular" multiplication, but we could have defined it differently. `3 * 3` still uses the `*` defined in `Int` – Dan Simon Jan 15 '12 at 16:23
0

Go visit Scala: How to define “generic” function parameters?

Community
  • 1
  • 1
Eastsun
  • 18,526
  • 6
  • 57
  • 81
0

Think about it. Multiplication have different properties on different objects. Integer multiplication, for example, is associative and commutative, scalar multiplication is not commutative. And multiplication on floating point numbers... But, of course, you can have what you want with trait or with "trait and implicit" construction which resembles a typeclass.

S. Kucherenko
  • 585
  • 3
  • 8