为什么Scala中没有用于单元素元组的Tuple1文本?

为什么Scala中没有用于单元素元组的Tuple1文本?,scala,syntax,tuples,Scala,Syntax,Tuples,Python为单个元素元组提供了(1,)。在Scala中,(1,2)适用于Tuple2(1,2),但我们必须使用Tuple1(1)来获得一个元素元组。这似乎是一个小问题,但设计期望产品的API对于传递单个元素的用户来说是一件痛苦的事情,因为他们必须编写Tuple1(1) 也许这是一个小问题,但Scala的一个主要卖点是用更少的输入来增加输入。但在这种情况下,它似乎是更多的打字与更多的打字 请告诉我: 1) 我错过了这个,它以另一种形式存在,或者 2) 它将被添加到该语言的未来版本中(他们将接受补

Python为单个元素元组提供了
(1,)
。在Scala中,
(1,2)
适用于
Tuple2(1,2)
,但我们必须使用
Tuple1(1)
来获得一个元素元组。这似乎是一个小问题,但设计期望产品的API对于传递单个元素的用户来说是一件痛苦的事情,因为他们必须编写Tuple1(1)

也许这是一个小问题,但Scala的一个主要卖点是用更少的输入来增加输入。但在这种情况下,它似乎是更多的打字与更多的打字

请告诉我: 1) 我错过了这个,它以另一种形式存在,或者
2) 它将被添加到该语言的未来版本中(他们将接受补丁)。

当然,您可以向API添加隐式转换:

implicit def value2tuple[A](x: A) = Tuple1(x)
我确实觉得奇怪,
Tuple1.toString
包含后面的逗号:

scala> Tuple1(1)
res0: (Int,) = (1,)

Python不是静态类型的,所以元组的行为更像是固定大小的集合。Scala不是这样,元组的每个元素都有不同的类型。元组在Scala中的用途与Python中的不同。

您可以定义隐式转换:

implicit def value2tuple[T](x: T): Tuple1[T] = Tuple1(x)
只有当参数的静态类型不符合方法参数的类型时,隐式转换才会应用。假设您的方法采用
产品
参数

def m(v: Product) = // ...
例如,转换将应用于非乘积值,但不适用于
元组2
。警告:所有案例类都扩展了
产品
特性,因此转换也不会应用于它们。相反,product元素将是case类的构造函数参数

Product
TupleX
类的最小上界,但如果要将隐式Tuple1转换应用于所有非元组,则可以使用类型类:

// 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 ...
}
如果您让方法返回产品,您可以看到转换是如何应用的:

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)

该死,你以秒数击败了我。:)如果您制作的DSL想要接受单个项或元组项,该怎么办。如果我使用value2tuple,那么一个tuple2->Tuple1[tuple2[a,B]],这不是我想要的。我想要非tuple->Tuple1[A]和TupleX->TupleX。我可以对value2tuple中的类型进行某种约束吗?我不明白Tuple1的目的是什么。@paradigmatic它允许一个值实现
产品
接口(例如,您可以将它传递给一个将
产品
作为参数的方法)。当然,这样会丢失静态类型。@paradigmatic有时第三方工具会实现需要任何算术元组作为输入的东西。例如,当使用breeze.linalg.DenseMatrix创建数字矩阵时,其中一个构造函数获取元组的可变参数列表,其中每个元组对应一行。如果您想要创建一个Nx1矩阵(单列,但作为矩阵而不是向量键入),那么您可能需要将一个单元素元组的可变列表传递给该构造函数。关键是,即使我们无法预测为什么有人会需要它,忽略它也太短视了。这种方法似乎是完整的,但我想知道为什么不使用一个隐式参数,例如have-toTuple-take(隐式m:TupleConverter[t]),它的隐式值与您所做的类似。这似乎会减少代码行数。对这种方法有何评论?我不确定我是否遵循了你的建议,但听起来你的意思是你的API方法仍然会使用
产品
参数。如果是这样的话,case类就不会被包装在Tuple1中,因为它们已经扩展了
产品
特性(因此不需要转换)。@Oscar Boykin可能会提供一种更简单的方法。也许这就是你对你的评论的想法?谢谢,亚伦。这个例子几乎是完全相关的。静态类型似乎与我无关。在scala中打印Tuple1时,它将打印为(1,)。您不能将其用作文本,这似乎不一致。@Scala类型的mkString不是有效的文本。例如,
List(“abc”)
打印为
List(abc)
。相反,它们的目标是可读性。在本例中,额外的逗号仅表示它代表一个元组。顺便说一句,后面的逗号在以前的几个版本中都是有效的,并且被删除了,因为它增加了太多的复杂性,但收获太少。@Oscar静态键入的要点是,
Tuple1
没有在Scala中使用。在Python中,它可以用作大小为1的固定宽度集合,但Scala中的元组不是集合,所以它没有任何用处。元组是产品实例,有点像集合,所以我不确定我是否同意这个论点。@Oscar所有产品提供的都是迭代器。迭代器不是集合,没有人使用元组作为其
产品
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)