Scala:对多态类型的单例实例不使用任何内容

Scala:对多态类型的单例实例不使用任何内容,scala,singleton,polymorphism,nothing,Scala,Singleton,Polymorphism,Nothing,给定一个多态性特征,比如 trait Transform[T] { def apply( t: T ) : T } 您可能希望实现各种专用实例,例如 case class Add[Double] extends Transform[Double] { def apply( t: Double ) ... } case class Append[String] extends Transform[String] { def apply( t: String ) ... } 现在一个经常需

给定一个多态性特征,比如

 trait Transform[T] { def apply( t: T ) : T }
您可能希望实现各种专用实例,例如

 case class Add[Double] extends Transform[Double] { def apply( t: Double ) ... }
 case class Append[String] extends Transform[String] { def apply( t: String ) ... }
现在一个经常需要的转换也是身份转换。与其为每个类型T专门化标识,不如为所有类型T只使用一个单例实例。我的问题是:在Scala中实现这一点的最佳方法是什么

到目前为止,我发现:看看List[T]是如何实现List.empty[T]和Nil的,我尝试使用Nothing作为类型T。这似乎是有道理的,因为Nothing是所有其他类型的子类型:

 object Identity extends Transform[Nothing] {
    def apply( t: Nothing ) = t
 }
这似乎奏效了。但是,无论我想在哪里按原样使用此实例,如下所示:

 val array = Array[Transform[String]]( Transform.Identity )
我得到编译器错误“类型不匹配;找到:Identity.type,required:Transform[String]”。为了使用它,我必须显式地强制转换它:

 ... Identity.asInstanceOf[Transform[String]]

我不确定这是最好的,甚至是“正确”的方法。谢谢您的建议。

由于
Transform[T]
中的类型参数
T
是不变的,
Transform[Nothing]
不是
Transform[String]
的子类型,因此编译器会对此进行投诉。但是在这里使用
Nothing
无论如何都没有意义,因为永远不会有
Nothing
的实例。那么,如何将一个传递给
apply
方法呢?你需要再投一次。我能看到的唯一选择是:

scala> def Id[A] = new Transform[A] { override def apply(t:A) = t }
Id: [A]=> java.lang.Object with Transform[A]

scala> Id(4)
res0: Int = 4

scala> Id("")
res1: java.lang.String = ""

正如@Kim Stebel指出的,
Transform[T]
T
中是不变的(这是因为
T
def apply(T:T):T)
中同时出现在协变和逆变位置,所以
Transform[Nothing]
不是
Transform[String]
的子类型,也不能被认为是

如果您主要关心的是每次调用Kim的
def Id[A]
时的实例创建,那么您最好的模型就是Predef中的
confirms

private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
示例REPL会话

scala> Id("foo")
res0: java.lang.String = foo

scala> Id(23)
res1: Int = 23

好的,使用工厂函数而不是单例实例是可行的,这就是我现在在代码中使用它的方式。但我看到列表使用了一个输入为Nothing(Nil)的单例,所以我认为这样可能更好。传递值不是问题:我会传递字符串或Double或任何我需要的东西。但是,关于类型差异问题,您是对的,这可能是关键。传递值是问题所在。您可以将字符串传递给不需要任何内容的方法,因为Nothing是字符串的子类型,反之亦然。@Kim Stebel我想您的意思是:您不能将字符串传递给不需要任何内容的方法…@Kim Stebel:您是对的。“传递值不是问题”的意思是,当我将值传递到apply()时,我已经持有一个已转换为适当类型的Identity实例,例如Identity[String]。感谢您的回答和评论。因此,投射单例对象是正确的方法。现在,您的代码使用Identity[Any]而不是Identity[Nothing]。这两种方法似乎都管用,就演员而言,这似乎无关紧要。那么,有正确的吗?在lib中,Nil是List[Nothing]类型。
Any
是正确的选择。您希望具有转换类型的值(即,
SingletonId.apply
将应用于某些对象),但无法说出它们的类型。另一方面,
Nil
中的
Nothing
捕获了一个事实,即空列表的头根本没有对应的值。
scala> Id("foo")
res0: java.lang.String = foo

scala> Id(23)
res1: Int = 23