How to use Scala's singleton-object types?

南笙酒味 提交于 2020-01-02 03:23:08

问题


I'm writing a class which serves as base class for a series of singleton objects. In each singleton objects, there will be vals representing certain properties, and I want to write a method which, for each singleton object, only accepts objects created by it.

So I have the following:

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]) = {...}
}

So far, so good. Then I want to declare one of these singleton objects:

object M extends Maker {
  val a = make
}

But then, if I try this:

M.accept(M.a)

then I get a compile-time error:

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

My questions:

  1. What's the type object com.test.M, and how is it different from com.test.M.type?
  2. How can I do this in a smarter way?

回答1:


Get with the times, my good man! I fixed this over 24 hours ago. Next I expect to see velociraptors chasing dodos, furiously cracking their buggy whips while looking up stock quotes on their pointcast screensavers.

The commit in question is: 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 
%



回答2:


Use this.type instead of M. This simplified example should work:

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!



回答3:


Your first question, "What's the type object com.test.M, and how is it different from com.test.M.type?", still hasn't been answered. I haven't found it documented in the spec, but it seems that the object M type is the internal type representing the class that is implicitly created when you define an object M. Of course, M is the only instance of that class so one would expect the object M type to be equivalent to M.type, but the compiler apparently does not see it that way.

The problem you're running into, as @retronym explained, is that the singleton type M.type is not inferred for the type parameter when you invoke your make method. This is for the same reason that String is inferred rather than v.type in the session below:

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

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

where identity is defined as

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



回答4:


This works:

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)

The secret "sauce" is using make[M.type] inside the singleton object.

@retronym deserves the credit for explaining this: How to correctly type-annotate this HList?



来源:https://stackoverflow.com/questions/4315678/how-to-use-scalas-singleton-object-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!