如何定义一个适用于Scala中不相关类型的函数?

时间:2016-09-09 14:19:54

标签: scala types

我想定义一个将* 2应用于其参数的函数,该函数适用于有意义的所有类型。我尝试使用结构类型:

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

适用于字符串:

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

但不是数字:

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. 为什么会收到此错误消息?
  2. 是否可以使用结构类型做我想做的事情?
  3. 是否有可能以其他方式进行?
  4. 编辑:通过“做我想要的”,我的意思是为现有的类型工作,例如数字和字符串,而不仅仅是我自己定义的类。

3 个答案:

答案 0 :(得分:3)

  1. *被翻译为$times,结构类型检查是否存在*方法,但(我认为这是一个错误)将其称为内部($times)表示)。这适用于String,因为 $times适用于他们。

  2. 此方法适用于名称仅包含字母的方法。

  3. ```

    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. 是的,惯用的答案是类型。你选择究竟“有意义”的是什么。它们可以应用于任何已有的类:
    2. ```

      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
      

      另请注意,结构类型使用reflection =&gt;很慢。

答案 1 :(得分:3)

您可以随时重载该方法。为了使它在REPL中起作用,你必须:paste作为一个块。

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

答案 2 :(得分:0)

我发现的另一种可能性是使用宏。从Scala 2.11.8开始,它们仍然是实验性的,according to Martin Odersky,不会以这种形式存在。当前的synax是笨拙的,但到目前为止它是唯一完全DRY的方法(* 2只写一次,并且该函数适用于支持此操作的所有类型。)

无论这是否是最佳解决方案,我都是为了完整性而发布:

import reflect.macros.Context
def doubleImpl[T](c: Context)(x: c.Expr[T]): c.Expr[T] = {
  import c.universe._
  c.Expr(q"${x.tree} * 2")
}
def double[T](x: T): T = macro doubleImpl[T]