Как я могу предоставить гарантию времени компиляции, что мой метод будет возвращать тот же объект, который он получает в Scala? - PullRequest
5 голосов
/ 31 июля 2011

В классе Scala я могу легко объявить тип возвращаемого значения метода как this.type, чтобы гарантировать, что он будет возвращать тот же объект, для которого вызывается:

class Foo {
  def bar: this.type = this
}

Есть ли способ, которым я могу аналогичным образом указать, что метод, который принимает данный параметр AnyRef, будет точно возвращать эту ссылку? Следующий фрагмент не дает этой гарантии, я могу вернуть любой экземпляр A:

def processAndReturn[A <: AnyRef](obj: A): A = {
  // work with obj
  obj
}

Ответы [ 4 ]

8 голосов
/ 31 июля 2011

предложение huitseeker действительно с опцией -Ydependent-method-types:

$ scala -Ydependent-method-types
Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_24).

scala>   def processAndReturn[A <: AnyRef](obj: A): obj.type = {
     |       // work with obj
     |       obj
     |   }
processAndReturn: [A <: AnyRef](obj: A)obj.type

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

Из этой ветки я понял, что экспериментально зависимые типы методов существуют уже некоторое время, и есть даже опубликованная статья, использующая их, но реализация может быть ошибочной. Были некоторые обсуждения о переработке основ системы типов Scala (см. слайды Адриана Мура о зависимых типах объектов). Если это сработает, я думаю, что зависимые типы методов (среди прочего) будут полностью кошерными в будущих версиях Scala.

3 голосов
/ 31 июля 2011

Полагаю, вы захотите указать тип возврата obj.type в своем методе выше, но, к сожалению, на данный момент это запрещено.Согласно спецификации, стр.47 :

(...) в настоящее время одноэлементные типы параметров метода могут появляться только в теле метода;поэтому зависимые типы методов не поддерживаются.

Может быть какой-то способ достичь того, чего вы хотите, но не в спецификации типа этого метода.

1 голос
/ 31 июля 2011

huitseeker и Kipton, вероятно, найдут прямой ответ, который вы ищете.

С учетом вышесказанного, единственный вариант использования, который я могу себе представить, - это если вы хотите объединить вызовы для одного и того же объекта и убедиться, что вы действительно работаете над одним и тем же объектом. Есть ли другие?

Например:

class Counter {
  var i = 0
  override def toString = i.toString
}

def incr(c: Counter): Counter = { c.i += 1; c }

val two = incr(incr((new Counter)))
// two: Counter = 2

Если я позвоню incr два раза подряд, я ожидаю получить два. Теперь давайте представим, что я работаю с недобросовестной функцией, такой как:

def incorrect_incr(c: Counter): Counter = { 
  c.i += 1
  new Counter // should return c but does not
}

val notTwo = incorrect_incr(incorrect_incr(new Counter))
// notTwo: Counter = 0

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

case class Id[T <: AnyRef](t:T) {
  def map[U](f: (T) => U): Id[T] = { 
    f(t)
    this
  }
  def get: T = t
}

val reallyTwo = Id(new Counter).map(incorrect_incr).map(incorrect_incr).get
// reallyTwo: Counter = 2

map в основном обеспечивает вызов функции для побочного эффекта, get разворачивает значение ...

0 голосов
/ 31 июля 2011

Вопрос в том, что если вы собираетесь вернуть то, что передали, почему бы просто не использовать void? В конце концов, у вас уже есть объект в вызывающем коде, не так ли?

...