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

Я могу:

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)
                ^

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

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
Другие вопросы по тегам