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# настолько нов, они просто еще не реализовали его. Никакого вреда нет
Я уточню мой комментарий в этом ответе.
Я утверждаю, что нет причин предполагать, что вы хотите перехватывать исключения и завершать некоторые ресурсы на том же уровне. Возможно, вы привыкли делать это таким образом на языке, на котором было удобно работать с обоими одновременно, но когда это происходит, это совпадение. Финализация удобна, когда вы не перехватываете все исключения из внутреннего блока.
try...with
предназначен для перехвата исключений, чтобы вычисления могли продолжаться в обычном режиме. Просто между этими двумя отношениями нет (во всяком случае, они идут в противоположных направлениях: поймали ли вы исключение или позволяете ему пройти?).Зачем вам вообще что-то дорабатывать? Разве GC не должен управлять ресурсами, на которые нет ссылок? Ах... но язык пытается дать вам доступ к системным примитивам, которые работают с побочными эффектами, с явным распределением и удалением. Вы должны перераспределить то, что вы выделили (во всех случаях)... Разве вы не должны обвинять гнилой интерфейс, который предоставляет система вместо F#, который в данном случае является только мессенджером?
Но это кажется слишком искусственным, это ясно демонстрирует, что "F# против try/with/finally". Это почему?
Я предполагаю, что F# может быть "против" обработки исключений вообще. Ради совместимости.NET он должен их поддерживать, но в принципе нет обработки исключений * в функциональном программировании.
Подбрасывание / отлов исключений означает выполнение "прыжков в никуда", которые даже не замечены системой типов, что в корне противоречит философии функциональности.
Вы можете использовать чисто функциональный (монадический) код для переноса исключений. Все ошибки обрабатываются через значения в терминах базовой системы типов и без скачков / побочных эффектов.
Вместо написания функции
let readNumber() : int = ...
которые могут генерировать произвольные исключения, вы просто заявите
let readNumber() : int option = ...
что делает эту точку автоматически понятной по сигнатуре типа.
* Это не означает, что мы не обрабатываем исключительные ситуации, это просто обработка исключений в.NET/C++.