«Подъемные» исключения для типов Option - PullRequest
0 голосов
/ 15 января 2019

F # и Scala действуют как гибридный язык, который часто используется для соединения слов традиционного объектно-ориентированного кода с функциональным кодом.

Концепция, которая больше относится к миру ОО, является исключением, тогда как во многих случаях функциональный мир предпочитает типы Option. Чтобы обернуть существующий библиотечный код, основанный на исключениях, и сделать его более функциональным, я бы, таким образом, хотел бы «снять» код, генерирующий исключения, чтобы вместо этого возвращать тип параметра.

В Scala есть хорошая библиотечная функция, которая «ловит все» и конвертирует в опцию. Это можно использовать так:

import scala.util.control.Exception._
val functionalVersion = allCatch opt myFunction

см. В Scala есть ли уже существующая библиотечная функция для преобразования исключений в Options?

Теперь, когда я перехожу на F #, у меня такое же требование, но я не могу найти существующую функцию полезности для этого, а также пытаюсь реализовать ее самостоятельно.

Я могу создать такую ​​обертку для функции модуля, иначе действие

let catchAll f = try Some (f()) with | _ -> None

Но проблема здесь в том, что я не хочу сначала оборачивать весь код исключения в действие.

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

// Wrap out-of-bounds exception with option type
let maybeGetIndex (array: int[]) (index: int) = catchAll (fun () -> array.[index])

maybeGetIndex [| 1; 2; 3 |] 10 // -> None

Однако было бы намного лучше, если бы можно было просто написать

(catchAll a.[index])

т.е. применить catchAll ко всему выражению до его оценки. (Scala может достичь этого с помощью параметров вызова по имени , которые отсутствуют в F #)

Итак, этот вопрос состоит из двух частей:

  1. Существует ли существующая библиотечная функция для включения исключений в параметр? типы?
  2. Есть ли языковая функция, которая позволила бы мне реализовать это?

1 Ответ

0 голосов
/ 15 января 2019

Прежде всего, я думаю, что это не правда, что «концепция, которая больше относится к объектно-ориентированному миру, является исключением» . Исключения существуют во многих функциональных языках семейства ML, и, например, OCaml довольно сильно полагается на них и использует их даже для определенных структур потоков управления. В F # это не так, потому что .NET-исключения несколько медленнее, но я вижу исключения, очень ортогональные к объектно-ориентированной / функциональной проблеме.

По этой причине я нахожу исключения, часто более предпочтительными, чем типы опций в F #. Недостатком является то, что меньше проверок типов (вы не знаете, что может генерировать), но есть и то, что язык обеспечивает хорошую встроенную поддержку языков для исключений. Если вам нужно обработать исключительные ситуации, исключения - хороший способ сделать это!

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

Тем не менее, вы можете определить конструктор выражений вычислений, который оборачивает код в тело и служит в качестве вашей catchAll функции с несколько более точным синтаксисом:

type CatchAllBuilder() = 
  member x.Delay(f) = try Some(f()) with _ -> None
  member x.Return(v) = v

let catchAll = CatchAllBuilder()

Это позволяет вам написать что-то вроде:

catchAll { return Array.empty.[0] }

Как упоминалось ранее, я бы не стал этого делать, потому что (i) я не думаю, что все исключения должны быть преобразованы в опции в F #, и (ii) он вводит незнакомый синтаксис, который могут делать новые члены команды (и будущие вы) быть сбитым с толку, но это, вероятно, самый хороший синтаксис, который вы можете получить.

[РЕДАКТИРОВАТЬ: Теперь рабочая версия с return - это несколько менее красиво, но, возможно, все еще полезно!]

...