Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala, generic tuple

Tags:

generics

scala

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

like image 707
Gigitsu Avatar asked Dec 12 '18 09:12

Gigitsu


People also ask

What is a Scala tuple?

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.

Can we have variables of different types inside of a tuple in Scala?

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.

What is use of tuple in Scala?

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.

What is a tuple in spark?

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.


2 Answers

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
}
like image 84
mkUltra Avatar answered Sep 28 '22 05:09

mkUltra


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.

like image 29
Travis Brown Avatar answered Sep 28 '22 05:09

Travis Brown