将Scala Generic类型限制为仅具有运算符重载的类型

时间:2015-09-24 20:17:04

标签: scala generics

是否可以将Scala泛型类型限制为仅重载特定运算符的类型?

示例:

def foo[T](...):T = {...}
foo(...) * .70

搜索Google尚未制定解决方案。我考虑将T限制为Numeric类型(或scala的等价物),但后来我想允许用户定义自定义类型并为这些运算符指定自己的行为。

2 个答案:

答案 0 :(得分:4)

使用类型类。

trait Multiply[A] {
  def multiply(x: A, y: Double): A
}

object Multiply {
  def apply[A](implicit instance: Multiple[A]): Multiply[A] = instance

  trait Ops[A] {
    def typeClassInstance: Multiply[A]
    def self: A
    def *(y: Double): A = typeClassInstance.multiply(self, y)
  }

  trait ToMultiplyOps {
    implicit def toMultiplyOps[A](target: A)(implicit tc: Multiply[A]): Ops[A] = new Ops[A] {
      val self = target
      val typeClassInstance = tc
    }
  }

  trait AllOps[A] extends Ops[A] {
    def typeClassInstance: Multiply[A]
  }

  object ops {
    implicit def toAllMultiplyOps[A](target: A)(implicit tc: Multiply[A]): AllOps[A] = new AllOps[A] {
      val self = target
      val typeClassInstance = tc
    }
  }
}

(请注意,这相当于Michael Pilquist的simulacrum库生成的内容,因此您并不需要所有这些样板文件)

然后您的用户可以执行此操作:

case class Foo(x: Int)

object Foo {
  implicit val multiplyFoo: Multiply[Foo] = new Multiply[Foo] {
    def multiply(foo: Foo, y: Double): Foo = ???
  }
}

Foo(42) * 3.1415

答案 1 :(得分:2)

是的,有所谓的结构类型。您可以按如下方式定义它们:

def foo[T <: {def yourOperator:Unit}(...) : T = {...}

其中yourOperator是方法定义的名称,单位是返回类型。在这个例子中,T将接受声明一个名为yourOperator的方法的每个类型,其中Unit为返回类型。

这可以用于例如处理可关闭的资源。想象一下下面的代码。

def operatorOnResource[T <: {def close : Unit}](resource: T) : Unit = {
    //do stuff with the resource
    resource.close
}

请注意,此示例也存在风险,因为T仅对其结构有约束。因此,即使T满足结构需要,关闭的语义也可能不同。