Монады - пример где бы были полезны...
От: Shmj Ниоткуда  
Дата: 29.08.19 02:29
Оценка: 1 (1) -1
Могли бы вы привести пример из жизни, где вы использовали монады не ради академической науки а для облегчения написания/поддержки кода?

Бывает такое?
Re: Монады - пример где бы были полезны...
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 29.08.19 09:09
Оценка: 5 (4)
Здравствуйте, Shmj, Вы писали:

S> для облегчения написания/поддержки кода?


Ну когда на хаскеле пишешь, они повсеместно, просто потому что это основной способ написания кода, который меняет какое-то состояние, бросает ошибки и делает ввод-вывод. Это как прозой говорить.

А из более интересных применений, недавно на работе писал парсер с использованием библиотеки монадных парсер-комбинаторов megaparsec, там код выглядел так:
pLam = do   params <- pParamDefs
            kw "=>"
            (ty, exp) <- try pTypedExpr <|> pUntyped pExpr
            return $ Lam params ty exp

pTypedExpr = do
            ty <- pType
            kw ":"
            exp <- pExpr
            return (Just ty, exp)

pAssign = do
            (ty,name) <- pPossiblyTypedName
            kw "="
            exp <- pExpr
            return $ Assign ty name exp

pFunDef = do
            name <- pName
            lam <- pLam
            return $ Assign Nothing name lam

pExpr = pArray <|> try pTable <|> pBlock <|> try pIf <|> try pLam <|> try pLet <|> makeExprParser pTerm operatorTable

pTable = between (symbol "{") (symbol "}") (Table <$> listOf pField)

pField = do nm <- pName
            kw ":"
            e <- pExpr
            return (nm, e)
...

Тут код сразу описывает и грамматику, и построение типизированного AST из нее. Каждая строчка тут или успешно что-то парсит, продвигаясь вперед по тексту и порождая значения, или, если во входном тексте что-то другое, что эта строка не ожидает, прерывается и переходит к следующей альтернативе в выражениях с несколькими альтернативами (разделенными <|>), откатываясь назад в тексте, когда необходимо. Вся эта логика передачи управления туда-сюда и перемещения по входному тексту упрятана в монаду и do-нотацию, за счет чего код не перегружен механикой телодвижений, остается лишь существенная для задачи логика.
Re: Монады - пример где бы были полезны...
От: Jack128  
Дата: 29.08.19 09:24
Оценка: +1 -1
Здравствуйте, Shmj, Вы писали:

S>Могли бы вы привести пример из жизни, где вы использовали монады не ради академической науки а для облегчения написания/поддержки кода?


S>Бывает такое?


Бывает, но в haskel'е. Для того, чтоб монады были полезны на практике нужен high kinded polimorphism, которого в жаба-шарпах нету
Re: Монады - пример где бы были полезны...
От: Mamut Швеция http://dmitriid.com
Дата: 29.08.19 09:31
Оценка: 5 (3) +4
S>Могли бы вы привести пример из жизни, где вы использовали монады не ради академической науки а для облегчения написания/поддержки кода?

S>Бывает такое?


Монады — они везде. Только Хаскелисты хотят, чтобы они были строго и матиематически верны определению и реализации, что есть в Хаскеле. На практике достаточно и такого определения: https://github.com/hemanth/functional-programming-jargon#monad

Собственно, емнип, практически монадой (или ис пользующим монады) является любой код, который позволяет делать


Result1 = f1()
Result2 = f2(Result1)
Result3 = f3(Result2)
...
ResultN = fN(ResultN-1)

if ResultN is Error:
  return Error
else:
  return ResultN


И такой код где-нибудь рано или поздно пишут все


dmitriid.comGitHubLinkedIn
Re[2]: Монады - пример где бы были полезны...
От: takTak  
Дата: 29.08.19 09:41
Оценка:
S>>Могли бы вы привести пример из жизни, где вы использовали монады не ради академической науки а для облегчения написания/поддержки кода?

S>>Бывает такое?


J>Бывает, но в haskel'е. Для того, чтоб монады были полезны на практике нужен high kinded polimorphism, которого в жаба-шарпах нету


а можно как-то поподробнее и "на пальцах"?

допустим, в яве -шарпах, мне не нравится такой код

if (a != null)
            {
                if (a.Prop1 != null)
                {
                    
                }
            }

с помощью Maybe / Option / Either- монад его можно переписать во что-то более удобочитаемое,
а о чём именно говоришь ты?
Re[3]: Монады - пример где бы были полезны...
От: Jack128  
Дата: 29.08.19 10:05
Оценка:
Здравствуйте, takTak, Вы писали:

S>>>Могли бы вы привести пример из жизни, где вы использовали монады не ради академической науки а для облегчения написания/поддержки кода?


S>>>Бывает такое?


J>>Бывает, но в haskel'е. Для того, чтоб монады были полезны на практике нужен high kinded polimorphism, которого в жаба-шарпах нету


T>а можно как-то поподробнее и "на пальцах"?


T>допустим, в яве -шарпах, мне не нравится такой код


T>
T>if (a != null)
T>            {
T>                if (a.Prop1 != null)
T>                {
                    
T>                }
T>            }
T>

T>с помощью Maybe / Option / Either- монад его можно переписать во что-то более удобочитаемое,
T>а о чём именно говоришь ты?

FileStream fileStream = new FileStream(...)
fileStream.WriteString("BlaBla")
как тут поможет обычный полиморфизм ?? да никак. Но он поможет если ты хочешь писать строки и в файл и в сеть и в память.
Так и с монадами. Ну ясно что у тя тут монада Maybe(nullable) — но для упрощения этого кода само понятие монад не нужно, нужно тупо захардкорить в язык операции над nullable (как это сделали в шарпе) и получить a?.Prop?.MyMethod();

А если ты хочешь написать код работающий с любыми монадами — вот тут становится интереснее

Вот тебе пример на псевдо шарпе, который умеет:
1) хай кайндед полимофизм.
2) умеет неявно lift'идь операторы для любого монадического типа, а не только для Nullable<> как сейчас. Тo есть M<int> a, b; a + b — преобразует в a.bind(x => b.bind(y => new M<int>(x + y)))
Теоретически я бы мог использовать query syntax, который суть — аналог do натации в haskel'е, но это сильно раздуло бы код, скрывая смысл.


M<int> Average<M<_>>(IEnumerable<M<int>> ints) {
   var sum = new M(0);
   var count = 0;
   foreach(var v in ints) {
      sum = sum + v;
      count++;
   }
   return count > ? (sum / count) : new M<int>(0);
}


Думаю понятно что эта функция делает.

теперь попробует её использовать:
IEnumerable<int> items = ....;
// Identity - то монада Identity. Хотя опять же для удобства иcпользования можно было бы считать, что любой тип сам по себе является монадой Identity, чтоб не делать подобных приседаний
Console.WriteLine(Average(items.Select(x => new Identity(x))));


IEnumerable<Nullable<int>> items2 = File.ReadAllLines("1.txt").Select(TryParseInt); // TryParseInt имеет сигнатуру int? TryParseInt(string s);
Console.WriteLine(Average(items2)); // если какая то строка не является числом - получится null


IEnumerable<Task<int>> items3 = new[] { GetIntAsync("www.google.com"), GetIntAsync("www.bind.com"), GetIntAsync("www.ya.ru") } ;
Console.WriteLine(await Average(items3));
Отредактировано 29.08.2019 10:07 Jack128 (Опечатка) . Предыдущая версия .
Re: Монады - пример где бы были полезны...
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 29.08.19 11:14
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Могли бы вы привести пример из жизни, где вы использовали монады не ради академической науки а для облегчения написания/поддержки кода?

S>Бывает такое?
Бывает:
List, Parser, Future, Maybe, один раз получилась error-монада
Ну и если цацкель, то там IO-монада, без нее никуда.
Re[4]: Монады - пример где бы были полезны...
От: takTak  
Дата: 29.08.19 11:57
Оценка:
J>А если ты хочешь написать код работающий с любыми монадами — вот тут становится интереснее

J>Вот тебе пример на псевдо шарпе, который умеет:

J>1) хай кайндед полимофизм.
J>2) умеет неявно lift'идь операторы для любого монадического типа, а не только для Nullable<> как сейчас. Тo есть M<int> a, b; a + b — преобразует в a.bind(x => b.bind(y => new M<int>(x + y)))
J> Теоретически я бы мог использовать query syntax, который суть — аналог do натации в haskel'е, но это сильно раздуло бы код, скрывая смысл.


J>
J>M<int> Average<M<_>>(IEnumerable<M<int>> ints) {
J>   var sum = new M(0);
J>   var count = 0;
J>   foreach(var v in ints) {
J>      sum = sum + v;
J>      count++;
J>   }
J>   return count > ? (sum / count) : new M<int>(0);
J>}
J>


J>Думаю понятно что эта функция делает.


J>теперь попробует её использовать:

J>
J>IEnumerable<int> items = ....;
J>// Identity - то монада Identity. Хотя опять же для удобства иcпользования можно было бы считать, что любой тип сам по себе является монадой Identity, чтоб не делать подобных приседаний
J>Console.WriteLine(Average(items.Select(x => new Identity(x))));
J>



про монаду Identity я совсем не понял, зачем она вообще?


lift'идь — это перевести к монадическому виду?

т.е. польза от "хай кайндед полимофизм" в том, что нужно меньше проверки типов или что-то другое?
Re[5]: Монады - пример где бы были полезны...
От: Jack128  
Дата: 29.08.19 14:06
Оценка:
Здравствуйте, takTak, Вы писали:


T>про монаду Identity я совсем не понял, зачем она вообще?


Ну монада — это что то типа интерфейса c методом bind, а Identity — реализация этого интерфейса. Я там выше написал во что транслируется код a + b. Если a имеет тип int, то такая трансформация невозможно, потому что у типа int нету метода bind. Значит нам нужна обертка с таким методом. Я написал её явно, но потенциально за меня её мог бы сгенерить компилятор.

T>lift'идь — это перевести к монадическому виду?


lift'инг операторов в c# — это возможность использовать операторы с типом Nullable<T>, если эти операторы определены для типа Т.
То есть так как для типа int определён оператор + , то я могу написать Nullable<int> x, y; x + y.
А у типа struct A {} оператора + нету, поэтому Nullable<A> x, y; x + y — не скомпилируется. Выше я пожелал чтоб операторы лифтились не только для NUllable, а для любого монадического типа

T>т.е. польза от "хай кайндед полимофизм" в том, что нужно меньше проверки типов или что-то другое?


Польза в том же, что и польза от обычных дженериков, но немного на другом уровне. Эта концепция вообще говоря к монадам отнашения не имеет. ТО есть может существовать и приносить пользу без них, а вот монадам без HKP(high kinded polimorphism) — туго.

Вернемся к примеру со средним. Немного изменю его
M<double> Average<M<_>>(IEnumerable<M<int>> ints) {
   var sum = new M<double>(0.0);
   var count = 0;
   foreach(var v in ints) {
      sum = sum + v;
      count++;
   }
   return count > ? (sum / count) : new M<double>(0);
}

то есть если я передал список Nullable<int> то на выходе должен получить Nullable<double> . Если на входе список из Task<int> то на выходе Task<double> должен быть. Обычными дженериками такого не добьёшься. А вот шаблонами из С++ наcколько я понимаю можно.

Вот этот момент, то что я дженериком аргументом передаю не конкретный тип (ну например Nullable<int>), а open generic type — вот это и есть полимофизм высшего порядка
Отредактировано 29.08.2019 14:12 Jack128 . Предыдущая версия .
Re: Монады - пример где бы были полезны...
От: scf  
Дата: 29.08.19 14:17
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Могли бы вы привести пример из жизни, где вы использовали монады не ради академической науки а для облегчения написания/поддержки кода?


S>Бывает такое?


Я активно использую монады в Scala.

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

Монады — они везде. Например, джавовые Optional и CompletableFuture — монады. скаловские Option, Either, Future — монады. cats IO, monix Task, scalaz ZIO — тоже монады.

Вот моя несколько однобокая попытка рассказать, что такое монада: https://habr.com/ru/post/454534/
Re[5]: Монады - пример где бы были полезны...
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 29.08.19 14:23
Оценка:
Здравствуйте, takTak, Вы писали:

T>т.е. польза от "хай кайндед полимофизм" в том, что нужно меньше проверки типов или что-то другое?


Польза в том, что можно писать обобщенный код, работающий с разными "контейнерами", т.е. абстрагироваться не только от типа элементов (как обычно в языках с генериками), но и от обобщенных типов контейнеров, получается дважды генерик — генерик, у которого параметр сам генерик. Или языком С++ шаблон, у которого есть параметр-шаблон.

Например, я пишу такую функцию:
> pair a b = return (a,b)

Ее тип автоматически выводится такой:
pair :: Monad m => a -> b -> m (a, b)


Она берет на вход два значения типов a и b, делает из них тупл (a, b) и заворачивает его в монаду ("контейнер") m.
А вот какую именно — тут я пока не указываю, я могу передать тип "контейнера" позже, при вызове. Так я могу получить список с туплом в виде единственного элемента

> pair 2 3 :: [(Int, Int)]
[(2,3)]

(после :: указывается тип, квадратные скобки означают список)

А могу опциональное значение Just с туплом внутри.
> pair 2 False :: Maybe (Int, Bool)
Just (2,False)


А могу и вовсе функцию из чего-то третьего в такой тупл:
> q = pair "hello" True :: Int -> (String, Bool)
> q 3
("hello",True)

Т.е. в зависимости от переданных типов аргументов и типа "контейнера", моя функция строит очень разные вещи, она опирается на ф-ю return из интерфейса переданной монады чтобы построить завернутое значение.

Теперь, что, если я не хочу никакой особый контейнер, а хочу вызвать мою ф-ю pair и получить тупл без заворачивания его в список, мэйби или еще что-нибудь хитрое? Мне надо передать какую-то монаду, но она не должна ничего особенного делать, просто содержать значение как есть. Это и есть монада Identity. Аналог struct Identity<T> { T value; }
Re[6]: Монады - пример где бы были полезны...
От: takTak  
Дата: 29.08.19 14:40
Оценка:
T>>т.е. польза от "хай кайндед полимофизм" в том, что нужно меньше проверки типов или что-то другое?

DM>Польза в том, что можно писать обобщенный код, работающий с разными "контейнерами", т.е. абстрагироваться не только от типа элементов (как обычно в языках с генериками), но и от обобщенных типов контейнеров, получается дважды генерик — генерик, у которого параметр сам генерик. Или языком С++ шаблон, у которого есть параметр-шаблон.


ок, где-то на интуитивном уровне я это понимаю, но я до сих пор не могу понять другого: читается ли вообще написанный таким образом код? Ведь если я насoздaвал кучу типов для того, чтоб различать между ними, то это,наверное, из-за того, что я структурирую модель определённым образом, если же есть конструкт, который работает с любыми контейнерами одинаково (кстати, что там с проверкой типов при HKP), то как такой код вообще кто-то может позже понять ? приведённые примеры понятны, но это как бы на уровне "хеллоу вёд", как это всё выглядит в промышленном масштабе или даже если ты сам смотришь на что-то написанное тобой пару месяцев назад? есть такой опыт?



DM>Теперь, что, если я не хочу никакой особый контейнер, а хочу вызвать мою ф-ю pair и получить тупл без заворачивания его в список, мэйби или еще что-нибудь хитрое? Мне надо передать какую-то монаду, но она не должна ничего особенного делать, просто содержать значение как есть. Это и есть монада Identity. Аналог struct Identity<T> { T value; }


т.е. Identity просто заворачивает значение так, что с полученным конейнером можно работать привычными функциональными способами типа map, bind etc?
Re[7]: Монады - пример где бы были полезны...
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 29.08.19 15:35
Оценка: 2 (1)
Здравствуйте, takTak, Вы писали:


T>ок, где-то на интуитивном уровне я это понимаю, но я до сих пор не могу понять другого: читается ли вообще написанный таким образом код? Ведь если я насoздaвал кучу типов для того, чтоб различать между ними, то это,наверное, из-за того, что я структурирую модель определённым образом, если же есть конструкт, который работает с любыми контейнерами одинаково (кстати, что там с проверкой типов при HKP), то как такой код вообще кто-то может позже понять ? приведённые примеры понятны, но это как бы на уровне "хеллоу вёд", как это всё выглядит в промышленном масштабе или даже если ты сам смотришь на что-то написанное тобой пару месяцев назад? есть такой опыт?


Дело привычки. Поначалу может быть сложно, потом становится обычным делом. Чаще всего такой обобщенный код возникает там, где надо принимать функцию, и хочется разрешить не только чистые ф-ии, но и с эффектами. А какие там эффекты заранее не знаешь, но они как раз монадами кодируются.
Скажем, есть у нас функция выбора подпоследовательности из списка по заданному предикату:
filter  ::              (a ->   Bool) -> [a] ->   [a]

А мы хотим, чтобы наш предикат не просто возвращал Bool, а еще писал что-то в лог, или считал какую-то статистику. Такая функция-предикат для интов может иметь тип
Int -> IO Bool
или
Int -> SomeState Bool
или еще какой эффект иметь. Нам пофиг какой, если этот эффект вписывается в интерфейс монады. Тогда делаем обобщенную ф-ию
filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]

которая умеет применять предикат с эффектом, и сама тоже этим эффектом становится заражена.

Такие ф-ии обычно небольшие и несложные, вполне можно разобраться.

Другой частый вариант абстрагирования от монад — когда нужно объединить несколько эффектов. Для этого можно параметром монады с одним эффектом сделать другую монаду (заранее не знаем какую), которая будет описывать другие эффекты. Тогда их можно сложить в такой слоеный пирог. Это монадные трансформеры. К ним тоже быстро привыкаешь.

T>т.е. Identity просто заворачивает значение так, что с полученным конейнером можно работать привычными функциональными способами типа map, bind etc?


Да, просто содержит какое-то значение без изменений, при этом реализуя все необходимые интерфейсы "контейнера"/"эффекта".
Re[8]: Монады - пример где бы были полезны...
От: takTak  
Дата: 29.08.19 15:49
Оценка:
DM>Такие ф-ии обычно небольшие и несложные, вполне можно разобраться.

DM>Другой частый вариант абстрагирования от монад — когда нужно объединить несколько эффектов. Для этого можно параметром монады с одним эффектом сделать другую монаду (заранее не знаем какую), которая будет описывать другие эффекты. Тогда их можно сложить в такой слоеный пирог. Это монадные трансформеры. К ним тоже быстро привыкаешь.


если несложные, то в чём профит? ведь можно было бы просто обернуть несколько вспомогательных функций вокруг основной, то и эффект был бы тот тоже, или нет?

а что за сфера-то, если не секрет, где ты хаскель в коммерческом плане используешь, если я , конечно, правильно понял ?
Re[9]: Монады - пример где бы были полезны...
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 29.08.19 16:34
Оценка:
Здравствуйте, takTak, Вы писали:

T>если несложные, то в чём профит? ведь можно было бы просто обернуть несколько вспомогательных функций вокруг основной, то и эффект был бы тот тоже, или нет?


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

T>а что за сфера-то, если не секрет, где ты хаскель в коммерческом плане используешь, если я , конечно, правильно понял ?


Это у меня сейчас исследовательский проектик по выводу типов для языка
Автор: D. Mon
Дата: 25.04.19
, который внутри конторы используется.
Re: Монады - пример где бы были полезны...
От: omgOnoz  
Дата: 29.08.19 17:41
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Могли бы вы привести пример из жизни, где вы использовали монады не ради академической науки а для облегчения написания/поддержки кода?


S>Бывает такое?


Сначала я думал, что монады — это ругательство.

Потом я научился Scala-е, а уже после узнал, что я использую монады и это не ругательство
Отредактировано 29.08.2019 17:44 omgOnoz . Предыдущая версия .
Re[2]: Монады - пример где бы были полезны...
От: IT Россия linq2db.com
Дата: 29.08.19 19:01
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Бывает, но в haskel'е. Для того, чтоб монады были полезны на практике нужен high kinded polimorphism, которого в жаба-шарпах нету


Кое-что есть в достаточной степени, чтобы кое-как кривенько поддерживать монады.
Если нам не помогут, то мы тоже никого не пощадим.
Re: Монады - пример где бы были полезны...
От: IT Россия linq2db.com
Дата: 29.08.19 19:02
Оценка: -2
Здравствуйте, Shmj, Вы писали:

S>Могли бы вы привести пример из жизни, где вы использовали монады не ради академической науки а для облегчения написания/поддержки кода?


Без полноценной языковой поддержки монады бесполезны.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Монады - пример где бы были полезны...
От: Jack128  
Дата: 29.08.19 20:29
Оценка: +1
Здравствуйте, IT, Вы писали:

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


J>>Бывает, но в haskel'е. Для того, чтоб монады были полезны на практике нужен high kinded polimorphism, которого в жаба-шарпах нету


IT>Кое-что есть в достаточной степени, чтобы кое-как кривенько поддерживать монады.


Ну до появления async/await я написал экстеншн методы для Task, чтоб эти таски можно было в query syntax использовать

Получилось весело:
var _1 = 
    from _2 in DoBlaBlaAsync()
    from _3 in DoOtherBlaBlaAsync()
    from x in GetBlaBlaAsync()
    from _4 in DoWorkAsync(x)
    select 1;


Re[2]: Монады - пример где бы были полезны...
От: Poopy Joe Бельгия  
Дата: 29.08.19 20:58
Оценка: -1
Здравствуйте, IT, Вы писали:

IT>Без полноценной языковой поддержки монады бесполезны.

Очень полезны. С языковой поддержкой их просто удобнее использовать.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.