список параметров (*) с ленивыми параметрами по имени?

Я могу:

scala> def foo( f: => String) = println(f)
foo: (f: => String)Unit

и я могу:

scala> def foo( f: String*) = f.map(println)
foo: (f: String*)Seq[Unit]

но я не могу:

scala> def foo( f: =>String* ) = f.map(println)
<console>:1: error: ')' expected but identifier found.
       def foo( f: =>String* ) = f.map(println)
                       ^

ни

scala> def foo( f: (=>String)* ) = f.map(println)
<console>:1: error: no by-name parameter type allowed here
       def foo( f: (=>String)* ) = f.map(println)
                ^

Есть ли другой способ сделать то, что я хочу? Почему это не разрешено?


person nairbv    schedule 01.05.2013    source источник


Ответы (1)


Здесь => означает, что параметр передается по имени. То, что вы пробовали, не разрешено просто потому, что =>String не является фактическим типом (в отличие от, скажем, ()=>String), и вы не можете создать Array[=>String]. Учитывая, что вариативный параметр x: T* внутри обрабатывается как массив, содержащий все значения параметра (как в x: Array[T]), это причина, по которой невозможность создать Array[=>String] (что не имеет смысла) также означает, что вариативный параметр f: (=>String)* невозможно.

Эту проблему можно решить с помощью небольшого класса-оболочки:

implicit class ByName[T]( getValue: => T ) extends Proxy {
  def apply(): T = getValue
  def self = apply()
}

Затем измените подпись вашего метода следующим образом:

def foo( fs: ByName[String]* )

При вызове вашего метода с несколькими аргументами все аргументы будут неявно заключены в экземпляр ByName, а затем вы можете вызвать apply, чтобы получить фактическое значение:

def foo( fs: ByName[String]* ) = fs foreach { f => println( f() ) }

Учитывая, что ByName расширяет Proxy для простых вещей, таких как вызов toString или проверка на равенство, вам даже не нужно вызывать apply. Таким образом, вы можете просто сделать:

def foo( fs: ByName[String]* ) = f foreach println
person Régis Jean-Gilles    schedule 01.05.2013
comment
это выглядит круто. Я понял, что мой вопрос был дубликатом, но мне нравится этот ответ. Я использую версию 2.9, которая не поддерживает implicit class, можно ли заставить ее работать в версии 2.9? - person nairbv; 01.05.2013
comment
похоже, мне просто нужно, чтобы неявное было отделено от определения класса. - person nairbv; 01.05.2013