Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a function that works on unrelated types in Scala?

Tags:

types

scala

I'd like to define a function that applies * 2 to its argument, that works for all types where it's meaningful. I tried using structural types:

import scala.language.reflectiveCalls
def double[T](x: Any{def * (arg0: Int): T}) = x * 2

It works for strings:

scala> double("a")
res85: String = aa

But not for numbers:

scala> double(4)
java.lang.NoSuchMethodException: java.lang.Integer.$times(int)
  at java.lang.Class.getMethod(Class.java:1778)
  at .reflMethod$Method1(<console>:18)
  at .double(<console>:18)
  ... 32 elided
  1. Why do I get this error message?
  2. Is it possible to do what I want using structural types?
  3. Is it possible to do it in some other way?

Edit: By "do what I want" I mean working for already existing types, such as numbers and strings, not just for classes that I define myself.

like image 932
michau Avatar asked Sep 09 '16 14:09

michau


2 Answers

  1. * is translated to $times, structural type checks existence of * method, but (I suppose that's a bug) calls it's internal ($times) representations). That works for String, because there is $times for them.

  2. This approach should work for methods with names that only contain letters.

```

import scala.language.reflectiveCalls
def double[T](x: Any{def test (arg0: Int): T}) = x.test(2)

class A { def test(i: Int) = i * 10 }
class B { def test(i: Int) = i * 20 }

scala> double(new A)
res0: Int = 20

scala> double(new B)
res1: Int = 40
  1. Yes, idiomatic answer is typeclasses. You choose what exactly "meaningfulness" is. And they can be applied to any already existing class:

```

trait Multiply[A]{
  def times(a: A, x: Int): A
}

implicit val MultString = new Multiply[String] { def times(a: String, x: Int) = a * x }
implicit val MultInt = new Multiply[Int] { def times(a: Int, x: Int) = a * x }

def double[T](t: T)(implicit mult: Multiply[T]) = mult.times(t, 2)

scala> double("aaaa")
res0: String = aaaaaaaa

scala> double(111)
res1: Int = 222

Also note that structural typing uses reflection => is quite slow.

like image 60
dveim Avatar answered Nov 09 '22 20:11

dveim


You could always just overload the method. To make it work in the REPL you have to :paste it in as a block.

def double(s:String):String = s * 2
def double[N](n:N)(implicit ev: Numeric[N]):N = {
  import Numeric.Implicits._
  n * ev.fromInt(2)
}

double("this")  // result: String = thisthis
double(3L)      // result: Long = 6
like image 40
jwvh Avatar answered Nov 09 '22 20:11

jwvh