Ответ 1
Что-то вроде этого - хорошая идея, но я бы сделал это методом:
def cleanly[A,B](resource: => A)(cleanup: A => Unit)(code: A => B): Option[B] = {
try {
val r = resource
try { Some(code(r)) }
finally { cleanup(r) }
} catch {
case e: Exception => None
}
}
(обратите внимание, что мы поймаем только один раз, если вы действительно хотите, чтобы сообщение было напечатано в одном случае, а не другом, тогда вам нужно поймать обоих, как и вы). (Также обратите внимание, что я занимаюсь только исключениями; catching Error
также обычно неразумный, поскольку из него почти невозможно восстановить.) Метод используется следующим образом:
cleanly(new FileOutputStream(path))(_.close){ fos =>
Iterator.continually(in.read).takeWhile(_ != -1).foreach(fos.write)
}
Так как он возвращает значение, вы получите Some(())
, если он преуспеет здесь (который вы можете игнорировать).
Изменить: чтобы сделать его более общим, я бы действительно получил его вместо Either
, поэтому вы получите исключение. Например:
def cleanly[A,B](resource: => A)(cleanup: A => Unit)(code: A => B): Either[Exception,B] = {
try {
val r = resource
try { Right(code(r)) } finally { cleanup(r) }
}
catch { case e: Exception => Left(e) }
}
Теперь, если вы получили Right
, все прошло хорошо. Если вы получите Left
, вы можете выбрать свое исключение. Если вы не заботитесь об исключении, вы можете использовать .right.toOption
для сопоставления его с опцией или просто использовать .right.map
или что угодно, чтобы работать с правильным результатом, только если он есть (как и с Option
), (Совпадение шаблонов - полезный способ справиться с Either
s.)