Как использовать типы одноэлементных объектов Scala? - PullRequest
6 голосов
/ 30 ноября 2010

Я пишу класс, который служит базовым классом для серии одноэлементных объектов.В каждом одноэлементном объекте будут значения, представляющие определенные свойства, и я хочу написать метод, который для каждого одноэлементного объекта принимает только созданные им объекты.

Итак, у меня есть следующее:

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. Как я могу сделать это умнее?

Ответы [ 4 ]

16 голосов
/ 01 декабря 2010

Успокойся, мой добрый человек! Я исправил это более 24 часов назад. Затем я ожидаю увидеть велоцирапторов, гоняющихся за додо, яростно взламывающих свои багги-кнуты, когда они смотрят котировки акций на скринсейверах pointcast.

Соответствующий коммит: 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 
%
8 голосов
/ 30 ноября 2010

Используйте 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!
3 голосов
/ 01 декабря 2010

На ваш первый вопрос «Что за тип object com.test.M и чем он отличается от com.test.M.type?» До сих пор не дан ответ. Я не нашел его документированным в спецификации, но кажется, что тип object M является внутренним типом, представляющим класс, который неявно создается при определении объекта M. Конечно, M является единственным экземпляром этого класса, поэтому можно ожидать, что тип object M будет эквивалентен M.type, но компилятор, очевидно, не видит его таким образом.

Проблема, с которой вы столкнулись, как объяснил @ retronym , заключается в том, что синглтонный тип M.type не выводится для параметра типа при вызове метода make. Это по той же причине, по которой 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 голосов
/ 30 ноября 2010

Это работает:

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 заслуживает похвалы за объяснение этого: Как правильно ввести аннотацию в HList?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...