2016-12-12 2 views
1

Каков наилучший способ написать следующее в Scala? Это не выглядит совершенно правильно для меня - сначала передняя декларация 2 вал, затем длинная линия создания PrintWriter, затем блок finally. Единственное, что это идиоматическое, является catch блок ...Самый идиоматический способ написать try/catch/наконец-то в Scala?

val outputStream = Try(fs.create(tmpFile)) 
val writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream.get))) 

if (outputStream.isFailure) { 
    logger.error(s"Couldn't open file: $tmpFile") 
} 

try { 

    features.foreach { 
    case (sectionName, modelRDD) => 
     writer.append("{" + sectionName + ", " + modelRDD.getNumPartitions + "}") 
    } 

} catch { 
    case e: Exception => 
    logger.error(s"Got exception", e) 
    throw e 

} finally { 
    outputStream.get.close() 
    writer.close() 
} 
+0

На каком основании они «выглядят совершенно правильно» для вас? – YoungSpice

+0

Я новичок в Scala. Может быть, все в порядке, но я надеялся на что-то более простое, как я уже упоминал: объявления 2 вал, строка создания писателя и блок '' 'finally''. Может быть, есть что-то более идиоматическое, что я мог бы узнать? – Frank

+0

С управлением ресурсами: http://jsuereth.com/scala-arm/usage.html – cchantep

ответ

4

Далее мы можем использовать контекст исходного Try выполнить полный операции ввода/вывода:

Во-первых, мы определим функцию, которая инкапсулирует наш процесс:

def safeFilePrint(tf: => OutputStream)(op: PrintWriter => Unit): Try[Unit] = { 
    val os = Try(tf) 
    val write = { 
     val writer = os.map(f => new PrintWriter(f)) 
     val writeOp = writer.map(op) 
     val flushOp = writer.map(_.flush) 
     writeOp.flatMap(_ => flushOp) 
    } 
    val close = os.map(_.close) 
    write.flatMap(_ => close) 
} 

И использование:

val collection = Seq(...) 
val writeResult = safeFilePrint(new FileOutputStream(new File("/tmp/foo.txt"))){w => 
    collection.foreach(elem => w.write(e) 
} 

Обратите внимание, что в отличие от исходного кода мы получаем результат операции записи. Либо writeResult будет Success(()), если все пойдет хорошо или Failure(exception) что-то пошло не так. Исходя из этого наше приложение может дополнительно решить, что делать.

Можно задаться вопросом: «Где finally В Java finally используется для обеспечения выполнения некоторого кода (обычно управления ресурсами) даже в том случае, если исключение, созданное в области try, приведет к следующему пути обработки исключений.

В Scala, используя конструкции типа Try, Either или наш собственный ADT, мы поднимаем обработку ошибок на уровень приложения. finally становится ненужным, так как наша программа может справиться с отказом как просто другое действительное состояние программы.

+0

Является ли 'Try' ленивым? Эта операция 'close' кажется чередующейся неверной иначе – Bergi

+0

@bergi Я не понимаю смысла. Могли бы вы объяснить? – maasg

+0

Наверное, я просто недостаточно Скала. Похоже, что '_.close' может работать до' op'. Зачем использовать 'flatMap' с постоянной функцией? Не должно быть нечто вроде 'os.map (новый PrintWriter (_)). Map (op) .flatMap (_ => os.map (_. Close))'? – Bergi

3

Наконец-то этот код был прочитан после прочтения ответа @ maasg, в котором подчеркивается монадический поток и более «симметричный». Он выглядит намного лучше, чем код в OP!

def safePrintToStream(gen: => OutputStream)(op: PrintWriter => Unit): Try[Unit] = { 
    val os = Try(gen) 
    val writer = os.map(stream => new PrintWriter(stream)) 
    val write = writer.map(op(_)) 
    val flush = writer.map(_.flush) 
    val close = os.map(_.close) 
    write.flatMap(_ => flush).flatMap(_ => close) 
} 
+0

Отличный код! Мне это понравится –

Смежные вопросы