Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is there no Tuple1 Literal for single element tuples in Scala?

Python has (1,) for a single element tuple. In Scala, (1,2) works for Tuple2(1,2) but we must use Tuple1(1) to get a single element tuple. This may seem like a small issue but designing APIs that expect a Product is a pain to deal for users that are passing single elements since they have to write Tuple1(1).

Maybe this is a small issue, but a major selling point of Scala is more typing with less typing. But in this case it seems it's more typing with more typing.

Please tell me: 1) I've missed this and it exists in another form, or 2) It will be added to a future version of the language (and they'll accept patches).

like image 992
Oscar Boykin Avatar asked Jun 23 '11 21:06

Oscar Boykin


People also ask

Can a tuple contain a single element?

To create a tuple with only one item, you have add a comma after the item, otherwise Python will not recognize the variable as a tuple.

Is tuple immutable in Scala?

In Scala, a tuple is a value that contains a fixed number of elements, each with its own type. Tuples are immutable.

Is tuple a collection in Scala?

Tuple is a collection of elements. Tuples are heterogeneous data structures, i.e., is they can store elements of different data types. A tuple is immutable, unlike an array in scala which is mutable.

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.


1 Answers

You can define an implicit conversion:

implicit def value2tuple[T](x: T): Tuple1[T] = Tuple1(x)

The implicit conversion will only apply if the argument's static type does not already conform to the method parameter's type. Assuming your method takes a Product argument

def m(v: Product) = // ...

the conversion will apply to a non-product value but will not apply to a Tuple2, for example. Warning: all case classes extend the Product trait, so the conversion will not apply to them either. Instead, the product elements will be the constructor parameters of the case class.

Product is the least upper bound of the TupleX classes, but you can use a type class if you want to apply the implicit Tuple1 conversion to all non-tuples:

// given a Tupleable[T], you can call apply to convert T to a Product
sealed abstract class Tupleable[T] extends (T => Product)
sealed class ValueTupler[T] extends Tupleable[T] { 
   def apply(x: T) = Tuple1(x) 
}
sealed class TupleTupler[T <: Product] extends Tupleable[T] { 
   def apply(x: T) = x 
}

// implicit conversions
trait LowPriorityTuple {
   // this provides a Tupleable[T] for any type T, but is the 
   // lowest priority conversion
   implicit def anyIsTupleable[T]: Tupleable[T] = new ValueTupler
}
object Tupleable extends LowPriorityTuple {
   implicit def tuple2isTuple[T1, T2]: Tupleable[Tuple2[T1,T2]] = new TupleTupler
   implicit def tuple3isTuple[T1, T2, T3]: Tupleable[Tuple3[T1,T2,T3]] = new TupleTupler
   // ... etc ...
}

You can use this type class in your API as follows:

def m[T: Tupleable](v: T) = { 
   val p = implicitly[Tupleable[T]](v) 
   // ... do something with p
}

If you have your method return the product, you can see how the conversions are being applied:

scala> def m[T: Tupleable](v: T) = implicitly[Tupleable[T]](v)
m: [T](v: T)(implicit evidence$1: Tupleable[T])Product

scala> m("asdf") // as Tuple1
res12: Product = (asdf,)

scala> m(Person("a", "n")) // also as Tuple1, *not* as (String, String)
res13: Product = (Person(a,n),)

scala> m((1,2)) // as Tuple2
res14: Product = (1,2)
like image 192
Aaron Novstrup Avatar answered Sep 21 '22 14:09

Aaron Novstrup