Scala,通用元组

时间:2018-12-12 09:18:18

标签: scala generics

我有一个通用方法,可以接受任何大小的任何元组,唯一的约束是该元组的第一个元素应为MyClass类型。

类似这样的东西:

trait MyTrait[T <: (MyClass, _*)] {
  getMyClass(x: T): MyClass = x._1
}

我已经尝试过了

trait MyTrait[T <: (MyClass, _) with (MyClass, _, _) with (MyClass, _, _) with ...] {
  getMyClass(x: T): MyClass = x._1
}

但出现错误unboud wildcard type

6 个答案:

答案 0 :(得分:11)

如果您想在没有样板或运行时反射的情况下执行此操作,那么Shapeless是您的最佳选择。您可以使用audioCtx= track.createMediaElementSource(audioElement); 类型类将类型级别的约束放在元组的第一个元素上:

IsComposite

然后:

import shapeless.ops.tuple.IsComposite

trait MustBeFirst

class MyClass[P <: Product](p: P)(implicit ev: IsComposite[P] { type H = MustBeFirst }) {
  def getMustBeFirst(x: P): MustBeFirst = ev.head(p)
}

如果需要使用特征,可以将scala> val good2 = (new MustBeFirst {}, "") good2: (MustBeFirst, String) = ($anon$1@7294acee,"") scala> val good3 = (new MustBeFirst {}, "", 123) good3: (MustBeFirst, String, Int) = ($anon$1@6eff9288,"",123) scala> val good4 = (new MustBeFirst {}, "", 'xyz, 123) good4: (MustBeFirst, String, Symbol, Int) = ($anon$1@108cdf99,"",'xyz,123) scala> val bad2 = ("abc", 123) bad2: (String, Int) = (abc,123) scala> new MyClass(good2) res0: MyClass[(MustBeFirst, String)] = MyClass@5297aa76 scala> new MyClass(good3) res1: MyClass[(MustBeFirst, String, Int)] = MyClass@3f501844 scala> new MyClass(good4) res2: MyClass[(MustBeFirst, String, Symbol, Int)] = MyClass@24e15478 scala> new MyClass(bad2) <console>:15: error: could not find implicit value for parameter ev: shapeless.ops.tuple.IsComposite[(String, Int)]{type H = MustBeFirst} new MyClass(bad2) ^ (用于“证据”)要求放在定义中而不是在构造函数中:

ev

现在,任何实例化trait MyTrait[P <: Product] { implicit def ev: IsComposite[P] { type H = MustBeFirst } } 的类都必须提供证据,证明MyTrait是一个以P作为第一个元素的元组。

答案 1 :(得分:4)

这有点不安全,但是在这种情况下,您可以使用“结构类型”:

trait MyTrait {
  def getMyClass(x: {def _1: MyClass}): MyClass = x._1
}

答案 2 :(得分:2)

Scala无法使用大小未知的通用元组,因为Products不继承主题本身。您可以尝试通过play json lib使用Shapeless或Products。

答案 3 :(得分:0)

您需要从Product继承特征,通过它您可以让productIteratorproductArityproductElement处理返回的值。这是一个例子

case class MyClass()

trait MyTrait[T <: Product] {
  def getMyClass(x: T): Option[MyClass] = 
                if(
                     x.productIterator.hasNext 
                                   && 
                     x.productIterator.next().isInstanceOf[MyClass]
                 ){
    Some(x.productIterator.next().asInstanceOf[MyClass])
  } else {
    None
  }
}

case class Test() extends MyTrait[Product]

您可以像这样调用

Test().getMyClass((MyClass(), 1,3,4,5))
//res1: Option[MyClass] = Some(MyClass())

Test().getMyClass((1,3,4,5))
//res2: Option[MyClass] = None

希望这对您有所帮助。

答案 4 :(得分:-1)

正如其他人所说,您可以使用无定形:

import shapeless.ops.tuple.IsComposite
import shapeless.syntax.std.tuple._

class MyClass(i : Int){
  def hello() = println(i)
}


object Tup extends App  {

  def getMyClass[P <: Product](p: P)(implicit ev: IsComposite[P]): MyClass = {
    if (p.head.isInstanceOf[MyClass]) p.head.asInstanceOf[MyClass] else throw new Exception()
  }
  val x= (new MyClass(1),5,6)
  val y = (new MyClass(2),7)

  val c = getMyClass(x)
  val c1 = getMyClass(y)
  c.hello()
  c1.hello()

  val c2 = getMyClass((1,5,6,7)) // exception
  c2.hello()

}

答案 5 :(得分:-1)

如果您正在寻找编译时间保证,那么这是以下情况的用例之一 无形,

您需要在build.sbt中添加Shapeless,

libraryDependencies ++= Seq("
  com.chuusai" %% "shapeless" % "2.3.3"
)

现在,您可以使用Shapeless来定义带有编译时间保证的类型安全getter,

scala> import shapeless._
// import shapeless._

scala> import ops.tuple.IsComposite
// import ops.tuple.IsComposite

scala> import syntax.std.tuple._
// import syntax.std.tuple._

scala> case class Omg(omg: String)
// defined class Omg

scala> val myStringTuple = ("All is well", 42, "hope")
// myStringTuple: (String, Int, String) = (All is well,42,hope)

scala> val myOmgTuple = (Omg("All is well"), 42, "hope")
// myOmgTuple: (Omg, Int, String) = (Omg(All is well),42,hope)

现在,如果您要使用特定类型的“第一个”吸气剂来丰富元组,

scala> implicit class GetterForProduct[B <: Product](b: B) {
     |   def getFirst[A](implicit comp: IsComposite[B] { type H = A }): A = b.head
     | }
// defined class GetterForProduct

scala> val myString = myStringTuple.getFirst[String]
// myString: String = All is well

scala>   val myOmgError = myOmgTuple.getFirst[String]
// <console>:24: error: could not find implicit value for parameter comp: shapeless.ops.tuple.IsComposite[(Omg, Int, String)]{type H = String}
//         val myOmgError = myOmgTuple.getFirst[String]
//                                             ^

scala>   val myOmg = myOmgTuple.getFirst[Omg]
// myOmg: Omg = Omg(All is well

如果您不需要隐式扩充,而只是在寻找一种将类型“锁定”在吸气剂中并将其用于相应类型的方法,

scala> trait FirstGetterInProduct[A] {
     |   def getFirst[B <: Product](b: B)(implicit comp: IsComposite[B] { type H = A }): A = b.head
     | }
// defined trait FirstGetterInProduct

scala> object firstGetterInProductForString extends FirstGetterInProduct[String]
// defined object firstGetterInProductForString

scala> object firstGetterInProductForOmg extends FirstGetterInProduct[Omg]
// defined object firstGetterInProductForOmg

// Right tuple with right getter,
scala> val myString = firstGetterInProductForString.getFirst(myStringTuple)
// myString: String = All is well

// will fail at compile time for tuple with different type for first
scala> val myOmgError = firstGetterInProductForString.getFirst(myOmgTuple)
// <console>:23: error: could not find implicit value for parameter comp: shapeless.ops.tuple.IsComposite[(Omg, Int, String)]{type H = String}
//          val myOmgError = firstGetterInProductForString.getFirst(myOmgTuple)
//                                                                 ^

scala> val myOmg = firstGetterInProductForOmg.getFirst(myOmgTuple)
// myOmg: Omg = Omg(All is well)