Почему оператор return требуется для правильной оценки этого оператора while? - PullRequest
4 голосов
/ 06 декабря 2011

Почему оператор return требуется, чтобы этот оператор while был оценили правильно? Следующее утверждение позволяет

import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.io.BufferedReader
import java.io.InputStreamReader

trait Closeable {
  def close ()
}

trait ManagedCloseable extends Closeable {
  def use (code: () => Unit) {
    try {
      code()
    }
    finally {
      this.close()
    }
  }
}

class CloseableInputStream (stream: InputStream)
extends InputStream with ManagedCloseable {
  def read = stream.read
}

object autoclose extends App {
  implicit def inputStreamToClosable (stream: InputStream):
    CloseableInputStream = new CloseableInputStream(stream)

  override
  def main (args: Array[String]) {
    val test = new FileInputStream(new File("test.txt"))

    test use {
      val reader = new BufferedReader(new InputStreamReader(test))

      var input: String = reader.readLine

      while (input != null) {
        println(input)
        input = reader.readLine
      }
    }
  }
}

Это выдает следующую ошибку из scalac:

autoclose.scala:40: error: type mismatch;
 found   : Unit
 required: () => Unit
      while (input != null) {
      ^
one error found

Похоже, что он пытается обработать блок после use как встроенное утверждение, а не лямбда, но я не совсем уверен, почему. Добавление return через некоторое время устраняет ошибку:

test use {
  val reader = new BufferedReader(new InputStreamReader(test))

  var input: String = reader.readLine

  while (input != null) {
    println(input)
    input = reader.readLine
  }

  return
}

И приложение работает как положено. Может кто-нибудь описать мне, что происходит там точно? Кажется, это должно быть ошибкой. Это было хотя сохраняется во многих версиях Scala (протестировано 2.8.0, 2.9.0, 2.9.1)

Ответы [ 4 ]

8 голосов
/ 06 декабря 2011

Это потому, что use объявлено как () => Unit, поэтому компилятор ожидает, что блок, который вы даете use, вернет что-то, удовлетворяющее этой сигнатуре.

Похоже, вы хотитепревратить весь блок в параметр по имени, для этого измените def use (code : () => Unit) на def use (code : => Unit).

3 голосов
/ 06 декабря 2011

Чтобы понять суть дела, блоки не являются лямбдами . Блок в Scala - это ограничитель области видимости , ничего более.

Если бы вы написали

test use { () =>
  val reader = new BufferedReader(new InputStreamReader(test))

  var input: String = reader.readLine

  while (input != null) {
    println(input)
    input = reader.readLine
  }
}

Тогда у вас будет функция (обозначенная () =>), которая ограничена блоком.

Если use был объявлен как

def use (code: => Unit) {

Тогда синтаксис, который вы использовали, сработал бы, но не из-за лямбды. Этот синтаксис указывает, что параметр передается по имени , что, грубо говоря, означает, что вы берете все выражение, переданное в качестве параметра (т. Е. Весь блок), и подставляете его для code внутри тела use. тип из code будет Unit, а не функцией, но параметр не будет передан по значению.

3 голосов
/ 06 декабря 2011

() => Unit - это тип объекта Function0, и вы требовали, чтобы выражение use было того типа, которым оно явно не является.=> Unit - это параметр по имени , который вы должны использовать вместо этого.

Вы можете найти мой ответ на этот вопрос полезным.

1 голос
/ 06 декабря 2011

return или return expr имеет тип Nothing. Вы можете заменить это на любой тип, так как он никогда не возвращает значение окружающему выражению, вместо этого он возвращает управление вызывающей стороне.

В вашей программе он маскируется под требуемый тип () => Unit.

Вот иногда удобное использование для этого (хотя вы можете быть запятнаны как однотипные, если вы используете его слишком часто, не говорите никому, что слышали это от меня!)

def foo(a: Option[Int]): Int = {
  val aa: Int = a.getOrElse(return 0)
  aa * 2
}

Для записи вы, вероятно, должны написать:

def foo(a: Option[Int]): Int =
  a.map(_ * 2).getOrElse(0)      

Вы можете получить представление о разуме компилятора, проверив вывод scala -Xprint:typer -e <one-liner>. Добавьте -Ytyper-debug, если вам нравится просеивать через множество выводов!

scala210 -Ytyper-debug -Xprint:typer -e 'def foo: Any = {val x: () => Any = { return }}'
... elided ...
   typed return (): Nothing
    adapted return (): Nothing to () => Any,
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...