Здравствуйте, 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>>
Здравствуйте, 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) смысла не имеет.