【问题标题】:How to use Scala's singleton-object types?如何使用 Scala 的单例对象类型?
【发布时间】:2011-05-17 23:19:26
【问题描述】:

我正在编写一个作为一系列单例对象的基类的类。在每个单例对象中,都会有代表某些属性的 val,我想编写一个方法,对于每个单例对象,该方法只接受由它创建的对象。

所以我有以下内容:

class Obj[M <: Maker]

class Maker {
  implicit val me: this.type = this
  def make[M <: Maker](implicit maker: M) = new Obj[M]
  def accept(obj: Obj[this.type]) = {...}
}

到目前为止,一切都很好。然后我想声明这些单例对象之一:

object M extends Maker {
  val a = make
}

但是,如果我尝试这个:

M.accept(M.a)

然后我得到一个编译时错误:

type mismatch; found : com.test.Obj[object com.test.M] required: com.test.Obj[com.test.M.type]

我的问题:

  1. object com.test.M 是什么类型,和com.test.M.type 有什么不同?
  2. 如何以更智能的方式做到这一点?

【问题讨论】:

  • 对于第 2 点:总是有可能使 Obj 成为 Maker 的嵌套类并删除类型参数,但我不希望这样,因为我需要传递 Obj我的示例中的类之外的对象的实例,我需要过滤类型参数。
  • 您能否提供一个可编译的示例?我可以复制并粘贴到 REPL 中吗?
  • 好问题:我在实现 HList 时遇到了同样的问题,HNil 的类型被推断为 object HNil 而不是 HNil.type .升级到 2.9 nightly build,现在一切正常。

标签: scala types object singleton


【解决方案1】:

与时俱进,我的好人!我在 24 小时前修复了这个问题。接下来,我希望看到迅猛龙追逐渡渡鸟,一边在他们的点播屏幕保护程序上查看股票报价,一边狂暴地鞭打他们的马车鞭子。

有问题的提交是:http://lampsvn.epfl.ch/trac/scala/changeset/23622

// 1130.scala
class Obj[M <: Maker]

class Maker {
  implicit val me: this.type = this
  def make[M <: Maker](implicit maker: M) = new Obj[M]
  def accept(obj: Obj[this.type]) = ()
}

object M extends Maker {
  val a = make
}

object Test {
  def main(args: Array[String]): Unit = {
    M.accept(M.a)
  }
}

// too old
% /scala/inst/scala-2.9.0.r23619/bin/scalac ./1130.scala 
./1130.scala:15: error: type mismatch;
 found   : Obj[object M]
 required: Obj[M.type]
    M.accept(M.a)
               ^
one error found

// fresh enough
% /scala/inst/scala-2.9.0.r23624/bin/scalac ./1130.scala 
%

【讨论】:

  • 不错!所以我认为这绝对是一个编译器错误,而不是我对游戏类型的心理表征的问题。
  • 好吧,没有人说这是一个错误。这是一个不受欢迎的行为,但它是指定的。
  • 很公平,但是在这种情况下,你能解释一下object MM.type之间的区别吗?
  • 在实现之外没有多大意义。从 r23624 开始,我什至不知道你会在哪里遇到“对象 M”:它不是你可以表达的类型,所以如果它没有被推断出来,那么它就成了幕后的类型。
【解决方案2】:

您的第一个问题“object com.test.M 是什么类型,与com.test.M.type 有什么不同?”,仍未得到解答。我没有在规范中找到它,但似乎object M 类型是表示在定义对象M 时隐式创建的类的内部类型。当然,M 是该类的唯一实例,因此人们会认为 object M 类型等同于 M.type,但编译器显然不这么认为。

作为@retronym explained,您遇到的问题是,当您调用make 方法时,不会为类型参数推断出单例类型M.type。这与在下面的会话中推断String 而不是v.type 的原因相同:

scala> val v = "asdf"                      
v: java.lang.String = asdf

scala> identity(v)
res0: java.lang.String  = asdf

identity 定义为

def identity[T](v: T) = v

【讨论】:

  • 有道理。但看起来在 2.9 中临时修复了这种行为,所以要么 object M 真的是 M.type 并且存在错误,要么类型推断略有变化。
  • 如果你写val v = "string",v作为String类型,而当你写val singleton = M时,单例的类型是M.type而不是object M,这也让我感到困惑,所以我期待我最初的工作示例。很高兴它会在 2.9
【解决方案3】:

这行得通:

class Obj[M <: Maker]

class Maker {
  implicit val me: this.type = this
  def make[M <: Maker](implicit maker: M) = new Obj[M]
  def accept(obj: Obj[this.type]) = ()
}

object M extends Maker {
  val a = make[M.type]
}

M.accept(M.a)

秘密“酱汁”在单例对象中使用make[M.type]

@retronym 值得称赞:How to correctly type-annotate this HList?

【讨论】:

  • 这里的隐式是多余的:它只会强制 M 成为 this.type ...在这种情况下,我们不妨直接使用 this.type,得到 Michel Krämer 的解决方案。
  • 实际上,当从M 调用make 时,我正在努力避免显式的[M.type]...
  • @Miles:我只是假设他已经对帖子的真实案例进行了简单化,但在实际代码中他需要它
【解决方案4】:

使用this.type 代替M。这个简化的例子应该可以工作:

class Obj[M <: Maker]

class Maker {
  def make() = new Obj[this.type]
  def accept(obj: Obj[this.type]) = println(obj)
}

object M extends Maker

object N extends Maker

M.accept(M.make()) //works!
M.accept(N.make()) //error! type mismatch!

【讨论】:

  • 谢谢,这很好用。但是为什么我的例子不起作用,我的推理有什么问题? object com.text.Mcom.text.M.type 到底有什么区别?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多