Результат против повышения в F # async? - PullRequest
0 голосов
/ 27 ноября 2018

Кажется, что есть два способа вернуть ошибки в async рабочем процессе: raise и Result.

let willFailRaise = async {
  return raise <| new Exception("oh no!")
}

let willFailResult = async {
  return Result.Error "oh no!"
}

Для вызывающего абонента обработка несколько иная:

async {
  try 
    let! x = willFailRaise
    // ...
  with error -> 
    System.Console.WriteLine(error)
}

async {
  let! maybeX = willFailResult
  match maybeX with
  | Result.Ok x -> 
    // ...
  | Result.Error error -> 
    System.Console.WriteLine(error)
}

Мои вопросы:

  • Каковы преимущества / недостатки каждогоподход?
  • Какой подход более идиоматичен для F #?

Ответы [ 3 ]

0 голосов
/ 28 ноября 2018

Это один из многих аспектов программирования на F #, который страдает от раскола ума в основе языка и его сообщества.

С одной стороны, у вас есть «F # .NET Framework language», где исключения - это механизм обработки ошибок, с другой - «F # функциональный язык программирования», который заимствует свои идиомы из мира Haskell.,Это откуда Result (также известный как Either).

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

0 голосов
/ 29 ноября 2018

Это зависит от того, о какой ошибке мы говорим.В основном существует три вида:

  • Ошибки домена (например, пользователь предоставил неверные данные, пользователь с таким адресом электронной почты уже зарегистрирован и т. Д.)
  • Ошибки инфраструктуры (например, невозможно подключитьсяв другой микросервис или БД)
  • Паника (например, NullReferenceException или StackOverflowException и т. д.), вызванная ошибками программистов.

Хотя оба подхода могут получить работуГотово, обычно ваша задача - сделать ваш код максимально документированным и легким для чтения.Это означает следующее:

  • Ошибки домена: определенно идет на Result.Эти "ошибки" ожидаются, они являются частью вашего рабочего процесса.Использование Result отражает ваши бизнес-правила в сигнатуре функции, что очень полезно.
  • Сбои инфраструктуры: это зависит.Если у вас есть микросервисы, то, вероятно, эти сбои ожидаются, и, возможно, будет удобнее использовать Result.Если нет - переходите к исключениям.
  • Паника: определенно Exception.Прежде всего, вы не можете покрыть все Result, вам в любом случае понадобится глобальный фильтр исключений.Второе - если вы попытаетесь покрыть все возможные паники - код станет очень опасной катастрофой, которая убьет весь смысл использования Result для ошибок домена.

Так что на самом деле ничего не имеетчто касается взаимодействия Async или C #, речь идет о читабельности и удобстве сопровождения кода.Что касается C # iterop - не волнуйтесь, в Result есть все методы, такие как IsError и так далее.Но вы всегда можете добавить метод расширения:

[<AutoOpen>] module Utils = type Result<'Ok, 'Error> with member this.Value = match this with | Ok v -> v | Error e -> Exception(e.ToString()) |> raise

0 голосов
/ 27 ноября 2018

Повышение

Преимущества

  • Лучшее взаимодействие .NET, так как выбрасывание исключений довольно распространено в .NET
  • Может создавать пользовательские исключения
  • Проще получить трассировку стека, так как она есть прямо сейчас
  • Вам, вероятно, придется в любом случае иметь дело с исключениями из библиотечного кода в большинстве стандартных асинхронных операций, таких как чтение с веб-страницы
  • Работаетс более старыми версиями F #

Недостатки:

  • Если вы не знаете, что это может вызвать исключение, вы можете не знать, как его перехватить.Это может привести к взрывам во время выполнения

Результат

Преимущества

Недостатки

  • Доступно только в F # 4.1 или более поздней версии
  • Трудно для не-F # языков использовать его
  • API для Result не является исчерпывающим.
    • Есть только функции bind, map и mapError
    • Некоторые функции, которые было бы неплохо иметь:
      • bimap : ('TSuccess -> 'a) -> ('TError -> 'e) -> Result<'TSuccess,'TError> -> Result<'a, 'e>
      • fold : ('TSuccess -> 'T) -> ('TError -> 'T) -> Result<'TSuccess, 'TError> -> 'T
      • isOk
...