Re[6]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 20.01.22 11:29
Оценка: 9 (1)
Здравствуйте, Serginio1, Вы писали:
S> Ну с точки зрения ФП все является функцией, а вот в ИП нет. Там я так понял, что есть импликит A к Try<A>
S>[Pure]
S>
S>        public static Try<A> Try<A>(A v) =>
S>            () => v;
S>

Нет, это не implicit, а просто непонятно зачем нужная перегрузка, которая превращает значение любого типа в ленивую функцию, которая возвращает это же значение, завёрнутое в Result<A> (а вот тут уже через implicit).
S>Что такое Try<A> я так и не нашел
Синоним для Func<Result<A>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Try из библиотеки LanguageExt.Core
От: maxkar  
Дата: 20.01.22 18:52
Оценка: 5 (1)
Здравствуйте, Sinclair, Вы писали:

S>Нет, это не implicit, а просто непонятно зачем нужная перегрузка, которая превращает значение любого типа в ленивую функцию, которая возвращает это же значение, завёрнутое в Result<A> (а вот тут уже через implicit).


Это я тоже могу объяснить. Ну не саму перегрузку (это бред, ее более удачный синоним в библиотеке — TrySucc), а ее функциональность. Такая штука нужна, чтобы в цепочках вычислений можно было делать альтернативы (if) и при этом все типы сходились. Для примера, мы хотим инициализировать какой-то внутренний справочник. В prod мы его будем загружать со внешнего сервиса. А в разработке используем хардкоженое значение. И все это будем делать на Try. В результате у нас будет что-то в духе

var Dictionary<string, string> hardcodedConfig = ...;

method Try<Dictionary<string, string>> getAppProperties() {
  // some implementation...
}

method Dictionary<string, string> loadConfigFromUrl(String url) {
  // some implementation, assume throws.
}

Dictionary<string, string> tryLoadConfig =
  getAppProperties()
    .Bind(appConf =>
      if (appConf.ContainsKey("dict.bootstrap.url")) {
        Try( () => loadConfigFromUrl(appConf["dict.bootstrap.url"]) ).Retry(3)
      } else {
        TrySucc(hardcodedConfig) // or Try(hardcodedConfig)
      }
    )


Retry добавлен чтобы хоть немного обосновать использование Try. По сигнатуре Bind у нас функция должна тоже возвращать Try (сравните с Map, там функция возвращает просто значение). Try имеет смысл для ветки с loadConfigFromUrl. А для hardcodedConfig смысла не имеет, но без него типы не сойдутся.

Конкретно здесь можно было бы сделать так (обратите внимание, Bind превратился в Map):
Dictionary<string, string> tryLoadConfig =
  getAppProperties()
    .Map(appConf =>
      if (appConf.ContainsKey("dict.bootstrap.url")) {
        Try( () => loadConfigFromUrl(appConf["dict.bootstrap.url"]) ).Retry(3).IfFailThrow
      } else {
        hardcodedConfig
      }
    )


Исключение выбросится из IfFailThrow, обработается и т.д. И с Try здесь проблем нет. Но это уже не идеоматический код. У типичной монады (не Try, а чего-то более сложного) нет экстракторов. А вот аналог TrySucc есть (у монады будет 4 вещи — Map, Apply, Bind, аналог TrySucc). Поэтому что-то похожее на первый код (за исключением Retry) работает со многими типами, а второй — нет. Для унификации структуры кода с другими монадами и сделаны Try<A>(A)/TrySucc<A>(A).

Пара Try<A>(Exception)/TryFail<A>(Exception) тоже сделана для похожих ситуаций. Когда часть веток идет дальше в вычисление (Try в нашем случае), а другая часть завершается рано. TryFail — специфика Try, у "просто монад" аналогов нет.

В коде без использования (или потенциального использования) Bind вся четверка Try<A>(A)/TrySucc<A>(A)/Try<A>(Exception)/TryFail<A>(Exception) смысла не имеет.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.