Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 14.01.22 09:38
Оценка:
using System.Diagnostics;
using static System.Console;
using static LanguageExt.Prelude;
Try(() => { WriteLine("DONE");return true;}).Match(_ => WriteLine("OK"), exn => WriteLine(exn));

Чтобы замыкание выполнилось нужно чтобы оно возвращало значение, т.е. либо обернуть в act(()=>{}) либо вернуть что то из метода
иначе метод не будет вызван, но Match отработает, т.е. будет казаться что все работает.
Это еще раз показывает как много в компиляторе C# косяков.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re: Try из библиотеки LanguageExt.Core
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 14.01.22 10:07
Оценка:
Здравствуйте, vaa, Вы писали:


vaa>
vaa>using System.Diagnostics;
vaa>using static System.Console;
vaa>using static LanguageExt.Prelude;
vaa>Try(() => { WriteLine("DONE");return true;}).Match(_ => WriteLine("OK"), exn => WriteLine(exn));
vaa>

vaa>Чтобы замыкание выполнилось нужно чтобы оно возвращало значение, т.е. либо обернуть в act(()=>{}) либо вернуть что то из метода
vaa>иначе метод не будет вызван, но Match отработает, т.е. будет казаться что все работает.
vaa>Это еще раз показывает как много в компиляторе C# косяков.

https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Value%20Monads/Try/Try/index.html#TryExtensions_0_Match_2
method Unit Match <A> (this Try<A> self, Action<A> Succ, Action<Exception> Fail)


Кстати а какой должен будет тип для void?
Try(() => { WriteLine("DONE");}).Match( a => {a.GetType(); WriteLine("OK")}, exn => WriteLine(exn));


Unit?

Может это подойдет
https://stackoverflow.com/questions/13928963/implement-f-interface-member-with-unit-return-type-in-c-sharp
и солнце б утром не вставало, когда бы не было меня
Отредактировано 14.01.2022 11:17 Serginio1 . Предыдущая версия . Еще …
Отредактировано 14.01.2022 10:09 Serginio1 . Предыдущая версия .
Re: Try из библиотеки LanguageExt.Core
От: samius Япония http://sams-tricks.blogspot.com
Дата: 14.01.22 10:53
Оценка: +6
Здравствуйте, vaa, Вы писали:


vaa>
vaa>using System.Diagnostics;
vaa>using static System.Console;
vaa>using static LanguageExt.Prelude;
vaa>Try(() => { WriteLine("DONE");return true;}).Match(_ => WriteLine("OK"), exn => WriteLine(exn));
vaa>

vaa>Чтобы замыкание выполнилось нужно чтобы оно возвращало значение, т.е. либо обернуть в act(()=>{}) либо вернуть что то из метода
О каком именно замыкании речь? В вышеприведенном коде я ни одного замыкания не вижу.

vaa>иначе метод не будет вызван, но Match отработает, т.е. будет казаться что все работает.

vaa>Это еще раз показывает как много в компиляторе C# косяков.
В чем именно претензии к компилятору C#? Есть какое-то расхождение его поведения со спецификацией языка? Мне кажется, что компилятор C# не в ответе за реализацию данного Match-а.
Re[2]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 17.01.22 02:06
Оценка: 9 (1)
Здравствуйте, Serginio1, Вы писали:


S>Кстати а какой должен будет тип для void?

S>
S>Try().Match( a => {a.GetType(); WriteLine("OK")}, exn => WriteLine(exn));
S>


S>Unit?

Да
S>Может это подойдет
S>https://stackoverflow.com/questions/13928963/implement-f-interface-member-with-unit-return-type-in-c-sharp
Ну тут странное решение(тянуть F#)
достаточно использовать fun
    Try(fun(() => { WriteLine("DONE");}))
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[2]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 17.01.22 02:32
Оценка:
Здравствуйте, samius, Вы писали:

S>О каком именно замыкании речь? В вышеприведенном коде я ни одного замыкания не вижу.


Как вообще отличить Lambda от Closure в коде?


vaa>>иначе метод не будет вызван, но Match отработает, т.е. будет казаться что все работает.

vaa>>Это еще раз показывает как много в компиляторе C# косяков.
S>В чем именно претензии к компилятору C#? Есть какое-то расхождение его поведения со спецификацией языка? Мне кажется, что компилятор C# не в ответе за реализацию данного Match-а.
Согласен, дело в деталях реализации библиотеки, но проблема также и на уровне языка.
Вот тут хороший пример https://habr.com/ru/post/511534/ (не косяки, а подводные камни )
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[3]: Try из библиотеки LanguageExt.Core
От: samius Япония http://sams-tricks.blogspot.com
Дата: 17.01.22 05:05
Оценка: 1 (1)
Здравствуйте, vaa, Вы писали:

vaa>Здравствуйте, samius, Вы писали:


vaa>Как вообще отличить Lambda от Closure в коде?

В коде одно является способом записи анонимной функции и с ним (конкретно в C#) ошибиться сложно, т.к. при этом используется практически уникальный синтаксис =>, если не путать его с короткой записью геттера свойства. Замыкание в коде присутствует неявно и подразумевает способ связывания переменных из лексического окружения в первоклассных функциях.
То есть, в коде мы можем обнаружить примеры использования этих концепций как независимо одно без другого, так и вместе два в одном.

S>>В чем именно претензии к компилятору C#? Есть какое-то расхождение его поведения со спецификацией языка? Мне кажется, что компилятор C# не в ответе за реализацию данного Match-а.

vaa>Согласен, дело в деталях реализации библиотеки, но проблема также и на уровне языка.
С самого начала C# проектировался как C-подобная, или даже Java-подобная версия Basic с возможностями Delphi+VCL. Никаких намеков на грядущие функциональные фичи, или хоть какую-то мультипарадигменность. Ни лямбды, ни замыкания, ни параметрический полиморфизм, ни даже система типов не проектировалсь изначально под функциональщину. Все вкорячивалось по месту с грузом обратной совместимости.

Хаскель изначально развивался как чисто функциональный язык со всеми этими штуками, заложенными изначально. При этом, еще и с ленивой стратегией вычислений.

Поэтому, наивно было бы предполагать, что библиотека, написанная для того, что бы сделать C# похожим на хаскель, вела бы себя органично в C# с учетом его исторического развития. Хотел бы я посмотреть, кстати, на библиотеку для Хаскеля, которая делает его подобным C#. Шутка, если что.

vaa>Вот тут хороший пример https://habr.com/ru/post/511534/ (не косяки, а подводные камни )

Опять-таки мы сравниваем язык, который не имел четкого вектора развития и обрастал своими фичами как Инспектор Гаджет девайсами с языком, который изначально был ориентирован быть функциональным (хоть и на императивной базе) с более стройной системой типов(точнее, их записью), с функциональными типами, вычислительными выражениями и т.п.
Т.е. F# концептуально больше был готов к асинхронщине, т.к. имел уже на борту монады и средства по работе с ними. А в C# более традиционным подходом является компиляторная магия, которая из спецсинтаксиса генерирует машины состояний аки с yield-ом. Судя по всему, кто-то собрал асинки сначала на yield генераторах для IEnumerable<T>, а потом уже под него вкорячили спец синтаксис и интегрировали это решение в язык.

а так — да, хорошо глумиться над async void-ом с позиций языка, в котором void изначально был персоной нон-грата.
Re[3]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.01.22 06:11
Оценка:
Здравствуйте, vaa, Вы писали:

vaa>Как вообще отличить Lambda от Closure в коде?

Это две ортогональные концепции.
Замыкание использует в теле переменные или аргументы из контекста метода, в котором строится замыкание. Туда же относится явное либо неявное использование this.
Нет такого использования — нет замыкания.
Lambda — это всего лишь синтаксис для записи анонимных методов. Можно делать замыкания без лямбд и лямбды без замыканий.



S>>В чем именно претензии к компилятору C#? Есть какое-то расхождение его поведения со спецификацией языка? Мне кажется, что компилятор C# не в ответе за реализацию данного Match-а.

vaa>Согласен, дело в деталях реализации библиотеки, но проблема также и на уровне языка.
vaa>Вот тут хороший пример https://habr.com/ru/post/511534/ (не косяки, а подводные камни )
Не очень понятно, что имеется в виду. Я не смог найти документацию по методу Try(), который вы используете. Ну, кроме упоминания о том, что монады TryXXX<A> задепрекейчены в пользу Aff<A> и Eff<A>.
И вообще почти вся библиотека постепенно становится бесполезной с выходом новых версий C#.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 17.01.22 06:23
Оценка:
Здравствуйте, samius, Вы писали:

S>Здравствуйте, vaa, Вы писали:


vaa>>Здравствуйте, samius, Вы писали:


vaa>>Как вообще отличить Lambda от Closure в коде?

S>В коде одно является способом записи анонимной функции и с ним (конкретно в C#) ошибиться сложно, т.к. при этом используется практически уникальный синтаксис =>, если не путать его с короткой записью геттера свойства. Замыкание в коде присутствует неявно и подразумевает способ связывания переменных из лексического окружения в первоклассных функциях.
S>То есть, в коде мы можем обнаружить примеры использования этих концепций как независимо одно без другого, так и вместе два в одном.

Насколько я понимаю, под связыванием в замыкании подразумевается запоминание значений связываемых переменных на момент определения замыкания,
а лямбда использует текущее на момент вызова значение связанной переменной.
Есть ли пример кода замыкания? т.к. все эксперименты с лямбдами и делегатами работают без замыкания.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[5]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.01.22 06:33
Оценка:
Здравствуйте, vaa, Вы писали:
vaa>а лямбда использует текущее на момент вызова значение связанной переменной.
Не значение, а саму переменную.
vaa>Есть ли пример кода замыкания? т.к. все эксперименты с лямбдами и делегатами работают без замыкания.

public static Func<int, int> MakeAdd(int x)
  => (int y) => x + y; 

[Fact]
public void TestAdd()
{
   Assert.Equal(42, MakeAdd(5)(37));
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 17.01.22 06:33
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, vaa, Вы писали:


vaa>>Как вообще отличить Lambda от Closure в коде?

S>Это две ортогональные концепции.
S>Замыкание использует в теле переменные или аргументы из контекста метода, в котором строится замыкание. Туда же относится явное либо неявное использование this.
S>Нет такого использования — нет замыкания.
S>Lambda — это всего лишь синтаксис для записи анонимных методов. Можно делать замыкания без лямбд и лямбды без замыканий.

Не обязательно
(setq a 10)
(setq f1 (lambda (b)
       (+ a b))) ;; (setq lexical-binding nil) даст (lambda (b) (+ a b))
;; (setq lexical-binding t) даст (closure (t) (b) (+ a b))
(funcall f1 1)

(let ((a 20))
  (funcall f1 1))
;; в первом случае 11 21
;; во втором случае 11 11


В C# не пойму как реализовать

S>>>В чем именно претензии к компилятору C#? Есть какое-то расхождение его поведения со спецификацией языка? Мне кажется, что компилятор C# не в ответе за реализацию данного Match-а.

vaa>>Согласен, дело в деталях реализации библиотеки, но проблема также и на уровне языка.
vaa>>Вот тут хороший пример https://habr.com/ru/post/511534/ (не косяки, а подводные камни )
S>Не очень понятно, что имеется в виду. Я не смог найти документацию по методу Try(), который вы используете. Ну, кроме упоминания о том, что монады TryXXX<A> задепрекейчены в пользу Aff<A> и Eff<A>.
не оно?
https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Value%20Monads/Try/Try/index.html#LanguageExt_0_Try_1
S>И вообще почти вся библиотека постепенно становится бесполезной с выходом новых версий C#.
Можете привести пример для данного случая?
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[5]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.01.22 06:48
Оценка:
Здравствуйте, vaa, Вы писали:


vaa>Не обязательно

vaa>
vaa>;(setq a 10)
vaa>;(setq f1 (lambda (b)
vaa>;       (+ a b))) ;; (setq lexical-binding nil) даст (lambda (b) (+ a b))
vaa>;;; (setq lexical-binding t) даст (closure (t) (b) (+ a b))
vaa>;(funcall f1 1)

vaa>;(let ((a 20))
vaa>;  (funcall f1 1))
vaa>;;; в первом случае 11 21
vaa>;;; во втором случае 11 11
vaa>;


vaa>не оно?

Оно, но по прежнему непонятно, что такое Try. Вот это вот — что за булшит?

delegate Try <A>

Синтаксис объявления делегатов в C# включает в себя типы аргументов и возвращаемого значения. Здесь нет ни того, ни другого — какая-то неведомая хтонь. Вообще вся дока написана на каком-то воображаемом языке.

vaa>https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Value%20Monads/Try/Try/index.html#LanguageExt_0_Try_1

S>>И вообще почти вся библиотека постепенно становится бесполезной с выходом новых версий C#.
vaa>Можете привести пример для данного случая?

Я бы возможно смог, но сначала надо понять, что именно пытается сделать "данный случай". Для меня это пока выглядит как "банка огурцы ложка майонез".
Ну, т.е. интуитивно-то я понимаю, чего может хотеться от монады Try. Но одной интуиции недостаточно.

А про бесполезность библиотеки я имел в виду не столько монадическую часть, сколько все вот эти Record, RecordType, инициализаторы иммутабл коллекций, а также Option<T>.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 17.01.22 07:00
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Оно, но по прежнему непонятно, что такое Try. Вот это вот — что за булшит?

https://github.com/louthy/language-ext/blob/101937cf4d634da86866fa2a9b9df599990ffea5/LanguageExt.Core/Monads/Alternative%20Value%20Monads/Try/Try/Try.cs#L13
Да, видимо генератор доков сломался
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[5]: Try из библиотеки LanguageExt.Core
От: samius Япония http://sams-tricks.blogspot.com
Дата: 17.01.22 08:04
Оценка:
Здравствуйте, vaa, Вы писали:

vaa>Здравствуйте, samius, Вы писали:


vaa>>>Как вообще отличить Lambda от Closure в коде?

S>>В коде одно является способом записи анонимной функции и с ним (конкретно в C#) ошибиться сложно, т.к. при этом используется практически уникальный синтаксис =>, если не путать его с короткой записью геттера свойства. Замыкание в коде присутствует неявно и подразумевает способ связывания переменных из лексического окружения в первоклассных функциях.
S>>То есть, в коде мы можем обнаружить примеры использования этих концепций как независимо одно без другого, так и вместе два в одном.

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

нет, речь именно о переменной с возможностью изменять ее значение.
vaa>а лямбда использует текущее на момент вызова значение связанной переменной.
разумеется. Но лямбда в C# — это лишь еще одна форма записи того, что можно записать по-другому. С помощью delegate, например. Или с недавних пор с помощью локальной функции.

vaa>Есть ли пример кода замыкания? т.к. все эксперименты с лямбдами и делегатами работают без замыкания.

Не понимаю, что значит пример кода замыкания. Замыкание — это техника, лежащая под капотом компилятора. Код может лишь указывать на то, что для исполнения его потребуется выполнить замыкание. Для того, что бы посмотреть, что выходит, можно воспользоваться декомпилятором.

Ниже пример того, что в замыкании участвует именно общая переменная, а не значение на момент определения замыкания.
void Main()
{
    var foo = Foo();
    var inc1 = foo();
    var inc2 = foo();
    
    inc1().Dump(); // 1
    inc2().Dump(); // 2
}

Func<Func<int>> Foo()
{
    int i = 0;
    return () => () => ++i;
}
Re[7]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.01.22 08:20
Оценка:
Здравствуйте, vaa, Вы писали:
vaa>Да, видимо генератор доков сломался
Там не в генераторе доков дело.
Я вроде понял в чём дело, но надо руками пощупать эту библиотеку.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.01.22 09:50
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, vaa, Вы писали:

vaa>>Да, видимо генератор доков сломался
S>Там не в генераторе доков дело.
S>Я вроде понял в чём дело, но надо руками пощупать эту библиотеку.
Да, посмотрел. Я так и думал. Проблема не в компиляторе, а в библиотеке, которая работает неожиданным для вас образом.
Компилятор работает корректно.
Присланная вами ссылка на проблемы async void методов тоже никакого отношения к проблемам приведённого вами сниппета не имеет.
Дизайн языка, в целом, тоже норм.

А вот разработчик библиотеки поленился написать код для идиоматической интеграции библиотеки в язык.
В чём тут проблема? В том, что у Prelude.Try есть две сигнатуры. Автор имел в виду, что "одна сигнатура будет покрывать функции, а другая — значения".
Но у нас же не вырожденный ФП-язык, где у любой функции 1 аргумент и 1 результат. А нормальный, современный язык программирования, в котором функция является объектом первого класса и поддерживает различное количество аргументов, и некоторое разнообразие в возвращаемых результатах.
Смотрите за руками:
Try((int x) => { WriteLine("DONE"); return x; }).Match(_ => WriteLine("OK"), exn => WriteLine(exn));

Возвращаемое значение — есть, а Match по-прежнему не работает!
А всё потому, что "функциональная" перегрузка Try ловит ровно одну сигнатуру фунций — Func<A>. А все остальные сигнатуры сваливаются в перегрузку Try<A>(A value).
И вообще, весь тип Try<A> — одно сплошное горе, обнять и плакать.

Для того, чтобы заменить выброс исключений на возврат альтернативных значений, нужно сделать столько же типов Try, сколько есть Func<...> и Action<...> типов.
И, соответственно, наплодить нужное количество перегрузок в Prelude.Try.
Ведь нужные вам перегрузки выглядят вот так:
public Result delegate Try(); // non-generic Result behaves as non-generic Task: it either captures nothing or an exception thrown from action
public static class Prelude
{
  public static Try Try(Action a)
    => ()=>  { try {a(); return Result.Success;} 
               catch(Exception e) { return Result.FromException(e);
  }
}

public static class TryExtenions
{
  public static void Match(this Try ma; Action succ; Action<Exception> Fail)
  {
    var res = self.Try();
    if (res.IsFaulted)
      Fail(res.Exception);
    else
      Succ();
  }
}

Заодно это убрало бы у вас дискард — который всё равно ничему не может быть равен, т.к. ваша WriteLine("DONE") ничего не возвращает.
А для того, чтобы заработал приведённый мной код, потребуются вот такие вот перегрузки:
public Result<A> delegate Try<T, A>(T t); 
public static class Prelude
{
  public static Try<T, A> Try<T, A>(Func<T, A> f) => (T x) => f(x);
}

public static class TryExtenions
{
  public static Func<T, R> Match<T, A, R>(this Try<T, A> ma; Func<A, R> succ; Func<Exception, R> fail)
    => (T x) => 
       {
      var res = self.Try(x); 
          return res.IsBottom ? Fail(res.Exception ?? new BottomException())
                : res.IsFaulted ? Fail(res.Exception)
                : Succ(res.Value);  
       }
}

Ну, то есть на самом деле надо копать — тут не очень понятно, как смешиваются функции, возвращающие Result<A>, с функциями, возвращающими просто A. У автора библиотеки с этим некоторый бардак — в частности, если один из параметров succ или fail сам выбросит исключение, оно неожиданным образом вылетит из Match, напугав автора кода — ведь он-то ожидал как раз избавления от исключений!
В общем, эта часть библиотеки выглядит сыро.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Try из библиотеки LanguageExt.Core
От: samius Япония http://sams-tricks.blogspot.com
Дата: 17.01.22 13:10
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Ну, то есть на самом деле надо копать — тут не очень понятно, как смешиваются функции, возвращающие Result<A>, с функциями, возвращающими просто A. У автора библиотеки с этим некоторый бардак — в частности, если один из параметров succ или fail сам выбросит исключение, оно неожиданным образом вылетит из Match, напугав автора кода — ведь он-то ожидал как раз избавления от исключений!


Есть версия, что это попытка воспроизвести Railway oriented programming (https://fsharpforfunandprofit.com/rop/)
т.е. Result — монадический тип аки Option<T>/Either для записи цепочки вычислений в виде монад.

S>В общем, эта часть библиотеки выглядит сыро.

А с учетом аспектов, изложенных вот тут (https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/), оно еще и не выглядит полезным.
Re[6]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 18.01.22 01:43
Оценка:
Здравствуйте, samius, Вы писали:

S>Ниже пример того, что в замыкании участвует именно общая переменная, а не значение на момент определения замыкания.

да, тупанул, действительно сложнее реализовать именно поведение лямбды
using static System.Console;
var x = 42;
Action<int> closure = a => { WriteLine("closure = {0}", a + x); };
Action<int> lambda = Lambda(x);
closure(5);
lambda(5);
x = 11;
closure(5);
lambda(5);

Action<int> Lambda(int a) =>
    x => { WriteLine("lambda = {0}", x + a); };

что в ILSpy выглядит
using System;

private static void <Main>$(string[] args)
{
    int x2 = 42;
    Action<int> closure = delegate(int a)
    {
        Console.WriteLine("closure = {0}", a + x2);
    };
    Action<int> lambda = Lambda(x2);
    closure(5);
    lambda(5);
    x2 = 11;
    closure(5);
    lambda(5);
    static Action<int> Lambda(int a)
    {
        return delegate(int x)
        {
            Console.WriteLine("lambda = {0}", x + a);
        };
    }
}


closure = 47
lambda = 47
closure = 16
lambda = 47


Получается через лямбда-синтаксис выразить лямбду невозможно?
☭ ✊ В мире нет ничего, кроме движущейся материи.
Отредактировано 18.01.2022 2:03 Разраб . Предыдущая версия .
Re[10]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.01.22 02:17
Оценка:
Здравствуйте, samius, Вы писали:

S>Есть версия, что это попытка воспроизвести Railway oriented programming (https://fsharpforfunandprofit.com/rop/)

S>т.е. Result — монадический тип аки Option<T>/Either для записи цепочки вычислений в виде монад.
Да, это именно так.

S>>В общем, эта часть библиотеки выглядит сыро.

S>А с учетом аспектов, изложенных вот тут (https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/), оно еще и не выглядит полезным.
Ага, согласен.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Try из библиотеки LanguageExt.Core
От: samius Япония http://sams-tricks.blogspot.com
Дата: 18.01.22 02:21
Оценка: 24 (2)
Здравствуйте, vaa, Вы писали:

vaa>Здравствуйте, samius, Вы писали:


S>>Ниже пример того, что в замыкании участвует именно общая переменная, а не значение на момент определения замыкания.

vaa>да, тупанул, действительно сложнее реализовать именно поведение лямбды

vaa>Получается через лямбда-синтаксис выразить лямбду невозможно?

Бррр...

В C#, раз речь о нем, лямбдой называется короткий способ записи функции. В базе любую функцию (на самом деле, нет), можно записать в виде лямбда выражения/оператора.
Что нельзя записать — функции, использующие unsafe, передачу параметров по ссылке и что-то еще. Здесь и сейчас это не важно. Для простоты будем считать, что любую.

С лямбдой на этом всё. Это способ записи, причем, мы всегда можем записать функцию по-другому. Например, delegate может описывать функции там же, где и лямбда.
Далее про лямбду можно забыть вовсе, она не интересна. Это просто сахар и только, у сахара нет поведения, которое можно наблюдать. Единственное, что мы можем наблюдать — так это то, что функция, записанная через лямбду, будет вести себя так же,как и функция, записанная делегатом или как-то еще.

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

Но если мы говорим о лямбда-исчислении и о том, что не всякую лямбду из лямбда-исчисления можно выразить в C#, то да, это справедливо. Но справедливо слишком, т.к. у нас разные системы типизации. Без конкретики сложно поддерживать беседу.

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

В последнем параграфе для лямбды не нашлось места вовсе. Все, что справедливо для функций, будет справедливо и для лямбд (в рамках C#).
Re[7]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.01.22 02:25
Оценка: 9 (1)
Здравствуйте, vaa, Вы писали:

vaa>Получается через лямбда-синтаксис выразить лямбду невозможно?


Вы "лямбдой" называете замыкание значения, а не ссылки? Тогда да, надо использовать синтаксис анонимных делегатов.
Примерно так:
using System;
using static System.Console;
var x = 42;
Action<int> closure = a => { WriteLine("closure = {0}", a + x); };
Func<Action<int>> lambda = ()=>{var y=x; return (a)=> WriteLine("lambda = {0}", a + y); };
closure(5);
lambda()(5);
x = 11;
closure(5);
lambda()(5);
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 18.01.22 02:37
Оценка:
Здравствуйте, Sinclair, Вы писали:
S>Смотрите за руками:
S>
S>Try((int x) => { WriteLine("DONE"); return x; }).Match(_ => WriteLine("OK"), exn => WriteLine(exn));
S>

S>Возвращаемое значение — есть, а Match по-прежнему не работает!

Да тут есть определенный косяк, что нужно учитывать императивное мышление шапристов, однако если разобраться, то код выполнился в этом случае, только не тот который предполагалосью

using LanguageExt;
using static System.Console;
using static LanguageExt.Prelude;

string message = "Done";
PrintMessage(message).Match(_ => WriteLine("OK"), exn => WriteLine(exn));
//что эквивалентно, благодоря fun
Try(fun(() => WriteLine(message))).Match(_ => WriteLine("OK"), exn => WriteLine(exn));

Try<Unit> PrintMessage(string message)
{
    return () =>
    {
        WriteLine(message);
        return unit;
    };
}


S>Ну, то есть на самом деле надо копать — тут не очень понятно, как смешиваются функции, возвращающие Result<A>, с функциями, возвращающими просто A. У автора библиотеки с этим некоторый бардак — в частности, если один из параметров succ или fail сам выбросит исключение, оно неожиданным образом вылетит из Match, напугав автора кода — ведь он-то ожидал как раз избавления от исключений!

Вот это не понял, если ваш код может бросить исключение что в этом пугающего? Очевидно, что мы ограждаемся от исключений только в вызове Try<A>.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[10]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.01.22 08:09
Оценка: 2 (1)
Здравствуйте, vaa, Вы писали:

vaa>Здравствуйте, Sinclair, Вы писали:

S>>Смотрите за руками:
S>>
S>>Try((int x) => { WriteLine("DONE"); return x; }).Match(_ => WriteLine("OK"), exn => WriteLine(exn));
S>>

S>>Возвращаемое значение — есть, а Match по-прежнему не работает!

vaa>Да тут есть определенный косяк, что нужно учитывать императивное мышление шапристов

Императивное мышление шарпистов тут ни при чём.

vaa>однако если разобраться, то код выполнился в этом случае, только не тот который предполагалось

Естественно. Код выполняется во всех случаях .

using LanguageExt;
using static System.Console;
using static LanguageExt.Prelude;

string message = "Done";
PrintMessage(message).Match(_ => WriteLine("OK"), exn => WriteLine(exn));
//что эквивалентно, благодоря fun
Try(fun(() => WriteLine(message))).Match(_ => WriteLine("OK"), exn => WriteLine(exn));

Try<Unit> PrintMessage(string message)
{
    return () =>
    {
        WriteLine(message);
        return unit;
    };
}

Ну, всё верно. Здесь вы вручную утаптываете ваш код в ту самую сигнатуру Func<A> — безаргументной функции, возвращающей одно значение.
Это по-прежнему никак не помогает нам обработать случай с функцией int->int.

S>>Ну, то есть на самом деле надо копать — тут не очень понятно, как смешиваются функции, возвращающие Result<A>, с функциями, возвращающими просто A. У автора библиотеки с этим некоторый бардак — в частности, если один из параметров succ или fail сам выбросит исключение, оно неожиданным образом вылетит из Match, напугав автора кода — ведь он-то ожидал как раз избавления от исключений!

vaa>Вот это не понял, если ваш код может бросить исключение что в этом пугающего? Очевидно, что мы ограждаемся от исключений только в вызове Try<A>.
Смысл не в том, чтобы "оградиться от исключений в каком-то коде". Идея railway programming — в том, что вместо исключений мы используем варианты возврата.
Для того, чтобы она работала (хотя бы в том ограниченном смысле, как она может вообще работать), её обработчиками в match должны тоже быть не плоские функции, а соответствующие Try-монады.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: Try из библиотеки LanguageExt.Core
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 18.01.22 09:40
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Для того, чтобы она работала (хотя бы в том ограниченном смысле, как она может вообще работать), её обработчиками в match должны тоже быть не плоские функции, а соответствующие Try-монады.


Ну в F# Каррирование упрощает работу https://lsreg.ru/shpargalka-po-f/
и солнце б утром не вставало, когда бы не было меня
Re[12]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.01.22 12:09
Оценка: 9 (1)
Здравствуйте, Serginio1, Вы писали:
S> Ну в F# Каррирование упрощает работу https://lsreg.ru/shpargalka-po-f/
Это не в F#, это вообще удивительный мир ФП, где других функций, в общем-то, и нету.
Основное преимущество такого подхода — концептуальная чистота.
Основной недостаток — это отсутствие внятного автодополнения и нормальных перегрузок по типам аргументов.
Отдельным бонусом идёт чудовищная неэффективность — АФАИК, порождение эффективного кода для функций в таком стиле несовместимо с CLR.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 19.01.22 03:01
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>>>
S>>>Try((int x) => { WriteLine("DONE"); return x; }).Match(_ => WriteLine("OK"), exn => WriteLine(exn));
S>>>

S>>>Возвращаемое значение — есть, а Match по-прежнему не работает!
S>Это по-прежнему никак не помогает нам обработать случай с функцией int->int.

Вот вообще непонятно кто так станет делать?
В вашем случае Try возвращает Func<int,int>. И(следите за руками) Match по-прежнему работает!
Но да согласен с появлением нормальных record в C# быть может данный кейс уже не так актуален.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[12]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 19.01.22 04:09
Оценка:
Здравствуйте, vaa, Вы писали:

vaa>Вот вообще непонятно кто так станет делать?

В каком смысле? Сигнатуры функций бывают различными. Вовсе не обязательно функция должна иметь 0 параметров.
vaa>В вашем случае Try возвращает Func<int,int>. И(следите за руками) Match по-прежнему работает!
Нет конечно, ничего не работает. WriteLine("DONE") не выполняется; аргумент Fail при исключении тоже не выполнится:
Try((int x) => { WriteLine("DONE"); throw new InvalidOperationException(); return x; }).Match(_ => WriteLine("OK"), exn => WriteLine(exn));

vaa>Но да согласен с появлением нормальных record в C# быть может данный кейс уже не так актуален.
Рекорды тут ни при чём.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[13]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 19.01.22 04:37
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Нет конечно, ничего не работает. WriteLine("DONE") не выполняется; аргумент Fail при исключении тоже не выполнится:

S>
S>Try((int x) => { WriteLine("DONE"); throw new InvalidOperationException(); return x; }).Match(_ => WriteLine("OK"), exn => WriteLine(exn));
S>

И откуда возьмется x чтобы лямбда посчиталась? результат работы try функция а не ее результат. хм... матч то тут причем?
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[14]: Try из библиотеки LanguageExt.Core
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.01.22 05:15
Оценка: +1
Здравствуйте, vaa, Вы писали:

vaa>Здравствуйте, Sinclair, Вы писали:


S>>Нет конечно, ничего не работает. WriteLine("DONE") не выполняется; аргумент Fail при исключении тоже не выполнится:

S>>
S>>Try((int x) => { WriteLine("DONE"); throw new InvalidOperationException(); return x; }).Match(_ => WriteLine("OK"), exn => WriteLine(exn));
S>>

vaa>И откуда возьмется x чтобы лямбда посчиталась? результат работы try функция а не ее результат. хм... матч то тут причем?
Здесь нет кода, вызывающего лямбду. Матч выполняет Try над первым аргументом, результатом будет вот эта лямбда, которая записана в Try. Матч должен выполнить либо первый, либо второй экшн и вернуть Unit.
Где-то в консоли должен быть ОК, тело лямбды никто не выполняет, исключение в нем не возбуждается. (Код не запускал, если что).
Re[14]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 19.01.22 05:23
Оценка: 6 (2)
Здравствуйте, vaa, Вы писали:
S>>Нет конечно, ничего не работает. WriteLine("DONE") не выполняется; аргумент Fail при исключении тоже не выполнится:
S>>
S>>Try((int x) => { WriteLine("DONE"); throw new InvalidOperationException(); return x; }).Match(_ => WriteLine("OK"), exn => WriteLine(exn));
S>>

vaa>И откуда возьмется x чтобы лямбда посчиталась? результат работы try функция а не ее результат. хм... матч то тут причем?
Всё верно.
Чего мы ожидаем от конструкции Try().Match()?
Что она превратит некую функцию, бросающую исключения, в функцию, которая исключений не бросает, а выполняет действия, указанные в match.
Результатом указанного кода должна стать Action<int>, которая при вызове на любом аргументе выводит "InvalidOperationException".
А в реальности результатом будет Unit. Увы.

Я вижу, вам сложно воспринимать код C#.
Давайте я напишу помедленнее.
Начнём с двух простых методов.
Func<int, int> foo = x => x % 2 == 0 ? 1 : throw new Exception("Oops");
Func<int> bar = () => foo(DateTime.Now.Millisecond);

Первый из них отображает int->int, и иногда бросает исключение.
Второй — всего лишь обёртка над первым, которая в качестве параметра использует текущее значение времени.

Давайте теперь попробуем посмотреть, что можно с ними сделать с помощью Try.
Начнём со второго:
// 1. Working with the parameterless function:
// 1.1. Wrapping into the Try monad:
var tryBar = Try(bar);
// 1.2 Direct execution (note the lack of params in Try):
var barResult = tryBar.Try(); // Result<int>
// 1.3. Coalescing exceptions into default value
var barFinalResult = barResult.Match(x => x, ex => 1);
Assert.Equal(1, barFinalResult);
// 1.4. Shortcut coalescing:
barFinalResult = tryBar.Match(x => x, ex => 1);
Assert.Equal(1, barFinalResult);
// 1.5. Returnless matching:
tryBar.Match(x => WriteLine(x), ex => WriteLine(ex));

удобная вроде штука — мы превратили Func<int> throws Exception сначала в Func<Result<int>>, затем просто в Result<int>, а потом и в просто int.
Ну, или если нам результат функции нужен был просто в рамках некоего побочного эффекта (вывод на экран), то мы это сделали в одну строку в 1.5.


Попробуем сделать что-то похожее с foo:
var tryFoo = Try(foo);

Логично ожидать аналогичного поведения — наверное, у tryFoo будет доступен метод Try(int), который тоже вернёт Result<int>, чтобы по нему можно было матчиться.
А методы Match должны принимать int в качестве дополнительного параметра.
Ну, то есть мне логичным видится делать даже не это, а строить обратно Func<int, int> либо Action<int>, в зависимости от сигнатур переданных в Match обработчиков.
Но это бы означало ленивость Match, что противоречит поведению того убогого Match, который реализован в этой библиотеке для функций в стиле bar.

Но нет! у tryFoo есть только один метод Try, у которого нет параметров. И возвращает он вовсе не Result<int>, а Result<Func<int, int>>, который полностью бесполезен для наших целей.
Мы, конечно, можем выковырять этот Func<int, int> из Result, но даже после Match он продолжает сыпать исключениями.
Никаким способом мы из tryFoo не можем получить ожидаемое — Func<int, int>, которая для любого аргумента вернёт 1.
Поэтому здравомыслие автора библиотеки вызывает вопросы.

Если что-то непонятно, спрашивайте.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 19.01.2022 5:31 Sinclair . Предыдущая версия .
Re: Try из библиотеки LanguageExt.Core
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 19.01.22 07:54
Оценка:
Здравствуйте, vaa, Вы писали:


https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Value%20Monads/Try/Try/index.html#TryExtensions_0_Match_2

method R Match <A, R> (this Try<A> self, Func<A, R> Succ, Func<Exception, R> Fail)


Я думаю, что проблема в Try<A>
А должно быть
Try<Func<A>>, Try<Action>
и солнце б утром не вставало, когда бы не было меня
Re[2]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 19.01.22 08:44
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, vaa, Вы писали:



S>https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Value%20Monads/Try/Try/index.html#TryExtensions_0_Match_2


S>
S>method R Match <A, R> (this Try<A> self, Func<A, R> Succ, Func<Exception, R> Fail)
S>


S>Я думаю, что проблема в Try<A>

S>А должно быть
S>
S>Try<Func<A>>, Try<Action>
S>

Собственно функция-конструктор Try<A> и возвращает функцию A.
Возможно нужно было Try<T> назвать по другому, например Do<T>
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[3]: Try из библиотеки LanguageExt.Core
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 19.01.22 09:53
Оценка:
Здравствуйте, vaa, Вы писали:


vaa>Собственно функция-конструктор Try<A> и возвращает функцию A.

vaa>Возможно нужно было Try<T> назвать по другому, например Do<T>
Я к тому, что ошибка должна быть на этапе компиляции
Try((int x) => { WriteLine("DONE"); return x; })
и солнце б утром не вставало, когда бы не было меня
Re[4]: Try из библиотеки LanguageExt.Core
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.01.22 10:25
Оценка: 18 (1) +1
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, vaa, Вы писали:



vaa>>Собственно функция-конструктор Try<A> и возвращает функцию A.

vaa>>Возможно нужно было Try<T> назвать по другому, например Do<T>
S> Я к тому, что ошибка должна быть на этапе компиляции
S>
S>Try((int x) => { WriteLine("DONE"); return x; })
S>

За счет чего?
https://github.com/louthy/language-ext/blob/main/LanguageExt.Core/Monads/Alternative%20Value%20Monads/Try/Try/Try.Prelude.cs#L28
Метод Try<A> принимает в качестве значения типа A функцию Func<int,int> и возвращает делегат, возвращающий это значение-функцию Func<int,int>.
Re[5]: Try из библиотеки LanguageExt.Core
От: Sinclair Россия https://github.com/evilguest/
Дата: 19.01.22 11:28
Оценка:
Здравствуйте, samius, Вы писали:
S>Метод Try<A> принимает в качестве значения типа A функцию Func<int,int> и возвращает делегат, возвращающий это значение-функцию Func<int,int>.
Можно попробовать убрать нафиг этот метод, т.к. непонятно, насколько он нужен. Пока что выглядит так, что он кроме граблей никакой пользы не приносит — но для полного понимания хотелось бы, конечно, примеров кода на этой библиотеке.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: Try из библиотеки LanguageExt.Core
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.01.22 11:56
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, samius, Вы писали:

S>>Метод Try<A> принимает в качестве значения типа A функцию Func<int,int> и возвращает делегат, возвращающий это значение-функцию Func<int,int>.
S>Можно попробовать убрать нафиг этот метод, т.к. непонятно, насколько он нужен. Пока что выглядит так, что он кроме граблей никакой пользы не приносит — но для полного понимания хотелось бы, конечно, примеров кода на этой библиотеке.
Что бы понять, насколько этот метод нужен, нужно понять, что мы делаем, и как будет выглядеть код без использования этого метода.
Есть такое ощущение, что мы пытаемся найти обстоятельства, в которых эта библиотека делает что-то интуитивно понятное, но неизвестно заранее, что именно. Но да, с помощью этой библиотеки слишком легко сделать из чего угодно Unit/void, не прибегая к усилиям, и даже не пытаясь вычислить что-нибудь. Но нужна ли такая библиотека для этого? C# и сам с этим справится на раз.
Re[15]: Try из библиотеки LanguageExt.Core
От: maxkar  
Дата: 19.01.22 21:27
Оценка: 116 (4) +1
Здравствуйте, Sinclair, Вы писали:

S>Чего мы ожидаем от конструкции Try().Match()?

S>Что она превратит некую функцию, бросающую исключения, в функцию, которая исключений не бросает, а выполняет действия, указанные в match.

А с чего это? Вообще, конечно, библиотека укуренная. В ней два момента. Во-первых, судя по наличию TryExtensions.Retry, это ленивое вычисление (наверное, более правильный термин — по требованию/on demand), которое форсируется (выполняется) вызовом определенных методов. Match и Try — это два из нескольких форсирующих методов. Во-вторых, если не смотреть на ленивость, Try это практически аналог Result. Отличие только в обработке исключений в Map. Result.Map(x => throw Exception("Today is a bad day")) выбросит исключение (наверное..., я в данной библиотеке ничего не гарантирую). А вот Try.Map(x => throw Exception("Today is a bad day")) вычислит TryFail(Exception("Today is a bad day")).

Вообще авторы молодцы — сделали три совершенно противоположных друг другу перегрузки. Try<A>(Exception e) == TryFail(e), Try<A>(A a) == TrySucc(a) и наконец правильный конструктор Try<A>(Func<A> a). Условно,

using Try<A> = Func<Result<A>>

method Try<A>(Exception e): Func<Result<A>> {
  return () => ResultFail(e);
}
method Try<A>(A a): Func<Result<A>> {
  return () => ResultSuccess(e);
}
method Try<A>(Func<A> fn): Func<Result<A>> {
  return () => {
    try {
      ResultSuccess(fn()) // close to TrySucc
    } catch(Exception e) {
      ResultFail(e) // close to TryFail
    }
  }
}

method R Match <A, R> (this Try<A> self, Func<A, R> Succ, Func<Exception, R> Fail) {
  // force computation
  val resultValue: Result<A> = self()
  // and process outcomes, same as resultValue.Match(Succ, Fail)
  if (resultValue.isSuccess()) {
    return Succ(resultValue.value);
  } else {
    return Fail(resultValue.exception);
  }
}

method A Try<A>(this Try<A> self) {
  return self();
}


Можно для чистоты сделать Try<A> оберткой над Fun<Result<A>>, добавится грязи в виде распаковки/запаковки но принципиально ничего не изменит.

S>Попробуем сделать что-то похожее с foo:

S>Логично ожидать аналогичного поведения — наверное, у tryFoo будет доступен метод Try(int), который тоже вернёт Result<int>, чтобы по нему можно было матчиться.

Ожидать — логично. Но перегрузки Try<A>(A) и Try<A>(Exception) не должны были бы существовать вообще, чтобы избежать данной проблемы.

S>А методы Match должны принимать int в качестве дополнительного параметра.

S>Ну, то есть мне логичным видится делать даже не это, а строить обратно Func<int, int> либо Action<int>, в зависимости от сигнатур переданных в Match обработчиков.
S>Но это бы означало ленивость Match, что противоречит поведению того убогого Match, который реализован в этой библиотеке для функций в стиле bar.

Я понимаю, откуда растут ожидания. Но, к сожалению, нет. Try — это ленивое вычисление значения. Если бы пришлось писать TrySucc(foo) непоняток было бы гораздо меньше.

S>Но нет! у tryFoo есть только один метод Try, у которого нет параметров. И возвращает он вовсе не Result<int>, а Result<Func<int, int>>, который полностью бесполезен для наших целей.

S>Мы, конечно, можем выковырять этот Func<int, int> из Result, но даже после Match он продолжает сыпать исключениями.
S>Никаким способом мы из tryFoo не можем получить ожидаемое — Func<int, int>, которая для любого аргумента вернёт 1.

Почему никаким? Это же обычный "хитрый функтор":
var tryFoo = Try(foo);
var appliedFoo = tryFoo.Map(fn => fn(DateTime.Now.Millisecond))
appliedFoo.Match(x => x, ex => 1)

Для такого просится extension method на Try<Funс<A, B>>, но в данную библиотеку его не положили. Ну и название метода Try тоже за гранью добра и зла. Execute/Evaluate/ToResult/Perform — лучше бы передавало семантику. ToResult был бы ближе всего, если бы не было on demand вычислений. Или бы не делали кучу "удобств" и заставляли писать вручную Try().Match(...). Может тоже было бы понятней.

S>Поэтому здравомыслие автора библиотеки вызывает вопросы.


В плане применимости — жить можно. Это же монада. Потому и нужно с ней работать, как с монадой — Map/Bind/Apply. А вот всякие extractors — только в конце вычислений. Но без for comprehension/do notation это все очень не практично.

S>Если что-то непонятно, спрашивайте.


Аналогично. Если что не понятно про монады — спрашивайте (на шарпе я не очень пишу ). В качестве упражнения всем интересующимся рекомендую написать базовые методы Map и Bind на основе определения Try как алиаса типа.
Re[4]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 20.01.22 01:33
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Я к тому, что ошибка должна быть на этапе компиляции

S>
S>Try((int x) => { WriteLine("DONE"); return x; })
S>


Почему? Разве функции не первоклассные сущности? Try это контейнер который может хранить все что вам захочется.
Иначе композировать не получится.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[16]: Try из библиотеки LanguageExt.Core
От: samius Япония http://sams-tricks.blogspot.com
Дата: 20.01.22 02:28
Оценка:
Здравствуйте, maxkar, Вы писали:

M>Ожидать — логично. Но перегрузки Try<A>(A) и Try<A>(Exception) не должны были бы существовать вообще, чтобы избежать данной проблемы.



Так уже все встает на свои места
Re[5]: Try из библиотеки LanguageExt.Core
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 20.01.22 08:26
Оценка:
Здравствуйте, vaa, Вы писали:

vaa>Здравствуйте, Serginio1, Вы писали:


S>> Я к тому, что ошибка должна быть на этапе компиляции

S>>
S>>Try((int x) => { WriteLine("DONE"); return x; })
S>>


vaa>Почему? Разве функции не первоклассные сущности? Try это контейнер который может хранить все что вам захочется.

vaa>Иначе композировать не получится.
Ну с точки зрения ФП все является функцией, а вот в ИП нет. Там я так понял, что есть импликит A к Try<A>
[Pure]
        public static Try<A> Try<A>(A v) =>
            () => v;


Что такое Try<A> я так и не нашел
https://github.com/louthy/language-ext/blob/main/LanguageExt.Core/Monads/Alternative%20Value%20Monads/Try/Try/Try.Prelude.cs#L28

Понравился ответ
http://rsdn.org/forum/dotnet/8175660.1
Автор: maxkar
Дата: 20.01.22
и солнце б утром не вставало, когда бы не было меня
Re[6]: Try из библиотеки LanguageExt.Core
От: vaa  
Дата: 20.01.22 08:59
Оценка: 9 (1)
Здравствуйте, Serginio1, Вы писали:


S>Что такое Try<A> я так и не нашел


https://github.com/louthy/language-ext/blob/101937cf4d634da86866fa2a9b9df599990ffea5/LanguageExt.Core/Common/Result/Result.cs#L39
☭ ✊ В мире нет ничего, кроме движущейся материи.
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...
Пока на собственное сообщение не было ответов, его можно удалить.