【问题标题】:Why do String literals conform to Scala Singleton为什么字符串字面量符合 Scala Singleton
【发布时间】:2012-07-21 18:29:41
【问题描述】:

无论明智与否,我正在编写一个我想只接受 Scala 单例的方法,即通过“对象”实现的对象,而不是类或特征的构造实例。它应该接受任何类型的 Scala 单例,因此“MySingleton.type”不会这样做。

我遇到了非常奇怪的构造“scala.Singleton”,它没有在 api 文档中记录,但似乎可以解决问题:

scala> def check( obj : Singleton ) = obj
check: (obj: Singleton)Singleton

scala> check( Predef )
res0: Singleton = scala.Predef$@4d3e9963

scala> check ( new java.lang.Object() )
<console>:9: error: type mismatch;
 found   : java.lang.Object
 required: Singleton
              check ( new java.lang.Object() )

scala> check( Map )
res3: Singleton = scala.collection.immutable.Map$@6808aa2d

scala> check( Map.empty[Any,Any] )
<console>:9: error: type mismatch;
 found   : scala.collection.immutable.Map[Any,Any]
 required: Singleton
              check( Map.empty[Any,Any] )

然而,相当莫名其妙(对我来说),字符串文字被接受为单例,而显式构造的字符串不是:

scala> check( "foo" )
res7: Singleton = foo

scala> check( new String("foo") )
<console>:9: error: type mismatch;
 found   : java.lang.String
 required: Singleton
              check( new String("foo") )

为什么字符串字面量符合 Singleton?我是否误解了 Singleton 类型应该指定的内容?

【问题讨论】:

  • 我不是很肯定,但我相信这是因为编译器默认实习他们。
  • 我第一次听说scala.Singleton,它不在文档中。我不会打赌它会做出任何特别的承诺。事实上,我不确定是否有任何硬性标准可以用来区分单例对象和类。你能详细说明你通过识别它们来达到什么目标吗?
  • (我的动机相当微不足道:我希望能够导出 Java 转发类的完全限定名称,因此我可以通过 Java 反射将单例方法用作静态方法。我不喜欢依赖重整/美元签名的名称,因为它们可能会改变。所以,我编写了一个实用程序,将单例类名称取消标记为 Scala 为 Java 提供的名称。如果重整改变,我只需要更新我的方法。方法是仅适用于 Scala 单例,因此如果类型系统能够强制执行它,我希望它在获取和分解类名之前需要一个单例。)
  • 顺便说一句,Scala 2.10 将提供一个反射 API 来提供帮助。
  • 您可能想知道check(1) 也有效。似乎所有原始值都被视为单例。 check(Double.NegativeInfinity) 也可以。

标签: scala


【解决方案1】:

首先,什么是单例类型?如果你认为一个类型是一组值,那么单例类型就是一个只有一个元素的集合。

最常见的是,顶级对象可以包含在这样的集合中。

scala> object X
defined module X

scala> X: X.type
res41: X.type = X$@131d1cb

scala> res41: Singleton
res42: Singleton = X$@131d1cb

更一般地说,stable值可以形成单例类型。

scala> object X { val y: String = "boo" }
defined module X

scala> X.y: X.y.type
res44: X.y.type = boo

scala> res44: Singleton
res45: Singleton = boo

如果ydefvar,则它不再符合条件,因为随着时间的推移值可能不同,因此编译器不能保证单例类型分类为one-and-只有一个值。

scala> object X { def y: String = "boo" }
defined module X

scala> X.y: X.y.type
<console>:12: error: stable identifier required, but X.y found.
              X.y: X.y.type
                     ^

scala> object X { var y: String = "boo" }
defined module X

scala> X.y: X.y.type
<console>:12: error: stable identifier required, but X.y found.
              X.y: X.y.type
                     ^

还有一个限制:AnyVals 不能形成单例类型,因为语言规范明确将它们限制为 AnyRef

Paul Phillips 一直在策划一个 branch,它允许您为文字表达单例类型。

val xs: Stream[0.type](0)
val ys: Stream[0.type](0, 1) // does not compile
val x = xs.head // inferred type is 0.type, we statically know that this can only be 0!

【讨论】:

  • 很有趣,谢谢。我真的很喜欢“.type”类型的想法。但我仍然不明白 scala.Singleton 的目的。正如 pst 上面指出的那样,类型应该描述所指对象的持久特征,而不仅仅是历史。很难看出一个类型的意义在于保证某事在某个时刻是引用所引用的集合中的单个元素。毕竟,通过在瞬态范围内分配给 val 来“强制转换”任何 var 是微不足道的。如果与 val ("val f = (a : Int) => 2 * a") 符合的 fcn 相同,为什么 def 会失败?
  • Singleton 的“用户端”目的是什么;有没有涉及这种“类型”的有用应用程序?
  • “毕竟,通过在瞬态范围内分配给 val 来将任何 var “强制转换”到 this 是微不足道的”。 var 当前值的“快照”是稳定的;这不会颠覆稳定性的概念。
【解决方案2】:

据我所知,在这种情况下,每个不可变引用都有资格作为单例,而不仅仅是字符串。例如,您可以使用invoke check(5)val foo = List(1,2,3); check(foo)。但是,var bar = List(1,2,3); check(bar) 将不起作用。

从这种行为来看,如果编译器可以确定它永远不会改变(或者在这种情况下是“最终的”),我会假设一个引用被认为是一个单例。

【讨论】:

  • 我不了解List 似乎应该检查表达式的type,而不是它计算的变量/字段..然而,前者确实通过了,而后者却失败了。是否有其他编译器魔术 wrt 单例和类型检查?规则在哪里指定?
  • 是的,真实而有趣(而且,正如 pst 所暗示的一种非常奇怪的类型系统行为)。任何 val 似乎都符合(包括引用可变类型的 val),而所有 var 都失败(包括启动为不可变文字的 var)。映射到 java 原语和字符串的文字似乎符合(包括 null、true/false 和 Class 文字),更复杂的 Scala-y 文字(例如符号、函数、XML 文字)不符合。看起来无论 Singleton 类型检查什么,它都不是我真正想要的。不过,我确实希望我能更好地理解它。
【解决方案3】:

我认为最简单的线索来自 Scala 参考中的第 3 章,第 3.2.1 节:

单例类型的形式为 p.type,其中 p 是指向 a 的路径 期望符合 (§6.1) 到 scala.AnyRef 的值。类型表示 由 null 和 p 表示的值组成的值集。

稳定类型要么是单例类型要么是已声明的类型 成为 trait scala.Singleton 的子类型。

稳定类型的概念很重要,该 trait 可以将原本不会被认为是稳定的事物声明为稳定。

【讨论】:

  • 谢谢。我想我必须阅读规范才能理解“稳定性”在这种情况下的含义以及为什么它很重要。再一次,考虑到 scala.Singleton 的可观察行为,我觉得这有点令人费解。
猜你喜欢
  • 2017-11-07
  • 1970-01-01
  • 1970-01-01
  • 2011-01-29
  • 2014-11-29
  • 2010-09-20
  • 1970-01-01
  • 2016-09-15
  • 1970-01-01
相关资源
最近更新 更多