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

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

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

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

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

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

Ответ 1

Как уже упоминалось, вы обычно используете 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 # на самом деле не нужно беспокоиться об этом.

Ответ 2

ортогональность? Вы можете просто вставить попытку внутри try-finally, как вы покажете. (Это то, что происходит на уровне ИЛ в любом случае, я думаю.)

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

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

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

Ответ 3

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

Эксперт .NET 2.0 IL Assembler от Сержа Лидина

См. гл. 14, Обработка управляемых исключений

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

пг. 300

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

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

Если вы испускаете обработчик fault в блоке исключений, который также содержит обработчик захвата или обработчик наконец, полученный код непроверяемый.

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

Ответ 4

Я объясню свой комментарий в этом ответе.

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

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

Ответ 5

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

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

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

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

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

let readNumber() : int = ...

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

let readNumber() : int option = ...

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

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