I have a generic method that can accept any tuple of any size, the only constraint is that the first element of this tuple should be of type MyClass
.
Something like this:
trait MyTrait[T <: (MyClass, _*)] {
getMyClass(x: T): MyClass = x._1
}
I've tried this
trait MyTrait[T <: (MyClass, _) with (MyClass, _, _) with (MyClass, _, _) with ...] {
getMyClass(x: T): MyClass = x._1
}
but I get the error unboud wildcard type
In Scala, a tuple is a value that contains a fixed number of elements, each with its own type. Tuples are immutable. Tuples are especially handy for returning multiple values from a method.
Thankfully, Scala already has a built-in tuple type, which is an immutable data structure that we can use for holding up to 22 elements with different types.
You can use Tuple to write a method that takes a List[Double] and returns the count, the sum, and the sum of squares returned in a three-element Tuple, a Tuple3[Int, Double, Double]. They are also useful to pass a list of data values as messages between actors in concurrent programming.
If you're new to functional programming, a Tuple is a group or collection of data -- it can contain two elements or more. Spark is written in Scala, which supports tuples.
It's a little bit unsafe but you can use Structural type in this case:
trait MyTrait {
def getMyClass(x: {def _1: MyClass}): MyClass = x._1
}
If you want to do this without either boilerplate or runtime reflection, Shapeless is your best bet. You can use the IsComposite
type class to put type-level constraints on the first element of a tuple:
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)
}
And then:
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)
^
If you need to use a trait, you can put the ev
(for "evidence") requirement inside the definition instead of in the constructor:
trait MyTrait[P <: Product] {
implicit def ev: IsComposite[P] { type H = MustBeFirst }
}
Now any class instantiating MyTrait
will have to provide evidence that P
is a tuple with MustBeFirst
as its first element.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With