F# конструкции обработки исключений

Почему F# не поддерживает блок try / with / finally?

Разве не имеет смысла что-то пробовать, иметь дело с тем исключением, которое оно выдает, хотя бы для регистрации исключения, а затем быть уверенным, что какой-то код выполняется после всего этого?

Конечно, мы можем сделать

try
    try
        ...
    with ex -> ...
finally
    ...

Но это кажется слишком искусственным, это ясно демонстрирует, что "F# против try/with/finally". Это почему?

5 ответов

Решение

Как уже упоминалось, вы обычно используете try-with-finally чтобы убедиться, что вы правильно освободили все ресурсы в случае исключения. Я думаю, что в большинстве случаев вы можете сделать это проще, используя use ключевое слово:

let input = 
  try
    use stream = new FileStream("C:\temp\test.txt");
    use rdr = new StreamReader(stream);
    Some(rdr.ReadToEnd())
  with :? IOException as e -> 
    logError(e)
    None

Я думаю, что это в основном причина, почему вам не нужно try-with-finally так часто, как вы бы на других языках. Но, конечно, в некоторых ситуациях это может понадобиться (но вы, конечно, можете избежать этого, создав экземпляр IDisposable использование объектных выражений (что синтаксически очень просто). Но я думаю, что это настолько редко, что команде F# на самом деле не нужно беспокоиться об этом.

Ортогональность? Вы можете просто вложить "попробуй с" в "попробуй наконец", как ты покажешь. (В любом случае, это то, что происходит на уровне IL).

Тем не менее, попытка с окончанием - это то, что мы можем рассмотреть в будущей версии языка.

Лично я столкнулся с желанием этого только пару раз, но когда вам это нужно, это немного утомительно, когда приходится делать дополнительные вложения / отступы. В общем, я нахожу, что я редко пишу код обработки исключений, и это обычно просто один или другой (например, наконец, чтобы восстановить инвариантную или другую транзакционную семантику, или 'catch' в верхней части приложения, чтобы записать исключение или показать пользовательская диагностика).

Но я не думаю, что здесь есть что почитать о дизайне языка.

Не вдаваясь в подробности, потому что большие детали в

Эксперт.NET 2.0 IL Ассемблер Серж Лидин

Смотри: Ch. 14, управляемая обработка исключений

"Обработчики finally и сбоев не могут мирно сосуществовать с другими обработчиками, поэтому, если у охраняемого блока есть обработчик finally или сбоев, у него не может быть ничего другого. Чтобы объединить обработчик finally или сбоев с другими обработчиками, необходимо вложить охраняемый и обработчик блокирует другие защищенные блоки, так, чтобы у каждого обработчика finally или ошибки был свой личный защищенный блок."

стр. 300

Try/catch использует обработчик ошибок, а try/finally использует обработчик finally.

Смотрите: метод ILGenerator.BeginFaultBlock

Если вы генерируете обработчик ошибок в блоке исключений, который также содержит обработчик перехвата или обработчик finally, результирующий код не поддается проверке.

Итак, можно считать, что все языки.net используют синтаксический surgar, и, поскольку F# настолько нов, они просто еще не реализовали его. Никакого вреда нет

Я уточню мой комментарий в этом ответе.

  1. Я утверждаю, что нет причин предполагать, что вы хотите перехватывать исключения и завершать некоторые ресурсы на том же уровне. Возможно, вы привыкли делать это таким образом на языке, на котором было удобно работать с обоими одновременно, но когда это происходит, это совпадение. Финализация удобна, когда вы не перехватываете все исключения из внутреннего блока. try...with предназначен для перехвата исключений, чтобы вычисления могли продолжаться в обычном режиме. Просто между этими двумя отношениями нет (во всяком случае, они идут в противоположных направлениях: поймали ли вы исключение или позволяете ему пройти?).

  2. Зачем вам вообще что-то дорабатывать? Разве GC не должен управлять ресурсами, на которые нет ссылок? Ах... но язык пытается дать вам доступ к системным примитивам, которые работают с побочными эффектами, с явным распределением и удалением. Вы должны перераспределить то, что вы выделили (во всех случаях)... Разве вы не должны обвинять гнилой интерфейс, который предоставляет система вместо F#, который в данном случае является только мессенджером?

Но это кажется слишком искусственным, это ясно демонстрирует, что "F# против try/with/finally". Это почему?

Я предполагаю, что F# может быть "против" обработки исключений вообще. Ради совместимости.NET он должен их поддерживать, но в принципе нет обработки исключений * в функциональном программировании.

Подбрасывание / отлов исключений означает выполнение "прыжков в никуда", которые даже не замечены системой типов, что в корне противоречит философии функциональности.

Вы можете использовать чисто функциональный (монадический) код для переноса исключений. Все ошибки обрабатываются через значения в терминах базовой системы типов и без скачков / побочных эффектов.

Вместо написания функции

let readNumber() : int = ...

которые могут генерировать произвольные исключения, вы просто заявите

let readNumber() : int option = ...

что делает эту точку автоматически понятной по сигнатуре типа.

* Это не означает, что мы не обрабатываем исключительные ситуации, это просто обработка исключений в.NET/C++.

Другие вопросы по тегам