Помогите разработчику C# понять: Что такое монада?

<input class="submit-btn" name='submit' type="submit" value="Submit">

Вам не хватает добавить имя, чтобы отправить кнопку, чтобы ваше дело if (isset([110]

Вам не хватает добавить имя, чтобы отправить кнопку, чтобы ваше дело [111] не удалось

POST['submit']) ) { не удалось

186
задан Charlie Flowers 23 March 2009 в 16:51
поделиться

5 ответов

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

Это - вид обмана.

Теперь, то объяснение поможет Вам понять большое намерение изображения монад, но дьявол находится в деталях. Как точно Вы вычисляете последствия? Иногда, это не симпатично.

Лучший способ дать обзор, как для кого-то привыкшего к императивному программированию должен сказать, что это помещает Вас в DSL, где операции, которые смотрят синтаксически как то, что Вы привыкли к внешней стороне монада, используются вместо этого для создания функции, которая сделала бы то, что Вы хотите, если Вы могли бы (например), записать в выходной файл. Почти (но не действительно), как будто Вы были строительными нормами и правилами в строке, чтобы позже быть eval'd.

11
ответ дан MarkusQ 23 November 2019 в 05:50
поделиться

Большая часть того, что Вы делаете в программировании весь день, комбинирует некоторые функции вместе для создания больших функций от них. Обычно у Вас нет только функций на Вашей панели инструментов, но также и других вещах как операторы, переменные присвоения и т.п., но обычно Ваша программа комбинирует вместе много "вычислений" к большим вычислениям, которые будут объединены вместе далее.

Монада является некоторым способом сделать это "объединение вычислений".

Обычно Ваш самый основной "оператор" для объединения двух вычислений вместе ;:

a; b

При высказывании этого, Вы имеете в виду, "сначала делают a, затем сделайте b". Результат a; b в основном снова вычисление, которое может быть объединено вместе с большим количеством материала. Это - простая монада, это - способ расчесать маленькие вычисления к большим. ; говорит, "делают вещь слева, затем сделайте вещь справа".

Другая вещь, которая может рассматриваться как монада на объектно-ориентированных языках, .. Часто Вы находите вещи как это:

a.b().c().d()

. в основном означает, "оценивают вычисление слева и затем называют метод справа на результате этого". Это - другой способ объединить функции/вычисления вместе, немного более сложный, чем ;. И понятие объединения в цепочку вещей вместе с . монада, так как это - способ объединить два вычисления вместе к новому вычислению.

Другая довольно общая монада, которая не имеет никакого специального синтаксиса, является этим шаблоном:

rv = socket.bind(address, port);
if (rv == -1)
  return -1;

rv = socket.connect(...);
if (rv == -1)
  return -1;

rv = socket.send(...);
if (rv == -1)
  return -1;

Возвращаемое значение-1 указывает на отказ, но нет никакого реального способа абстрагировать эту проверку ошибок, даже если у Вас есть много вызовов API, которые необходимо объединить этим способом. Это - в основном просто другая монада, которая комбинирует вызовы функции по правилу, "если функция на левых возвратилась-1, действительно возвратите-1 самостоятельно, иначе вызовите функцию справа". Если у нас был оператор >>= это сделало эту вещь, которую мы могли просто записать:

socket.bind(...) >>= socket.connect(...) >>= socket.send(...)

Это сделало бы вещи более читаемыми и помогло бы абстрагировать наш специальный способ объединить функции, так, чтобы мы не должны были повторять нас много раз.

И существует намного больше способов объединить функции/вычисления, которые полезны как общий шаблон и могут быть абстрагированы в монаде, позволив пользователю монады написать намного более краткий и код разъединения, так как вся бухгалтерия и управление используемыми функциями сделан в монаде.

Например, вышеупомянутое >>= мог быть расширен, чтобы "сделать проверку ошибок и затем назвать правую сторону на сокете, который мы получили, как введено", так, чтобы мы не должны были явно указывать socket много времен:

new socket() >>= bind(...) >>= connect(...) >>= send(...);

Формальное определение немного более сложно, так как необходимо волноваться о том, как получить результат одной функции как вход к следующему, если та функция, потребности, которые вводят и так как Вы хотите удостовериться, что функции Вы объединяетесь, вписываются в способ, которым Вы пытаетесь объединить их в своей монаде. Но фундаментальное понятие просто, что Вы формализуете различные способы объединить функции вместе.

146
ответ дан sth 23 November 2019 в 05:50
поделиться

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

4
ответ дан TheMissingLINQ 23 November 2019 в 05:50
поделиться

Можно думать о монаде как о C# interface то, что классы должны реализовать. Это - прагматический ответ, который игнорирует всю категорию теоретическая математика позади, почему Вы хотели бы принять решение иметь эти объявления в своем интерфейсе и игнорируете все причины, почему Вы хотели бы иметь монады на языке, который старается избегать побочных эффектов, но я нашел, что это было хорошим началом как кем-то, кто понимает (C#) интерфейсы.

0
ответ дан hao 23 November 2019 в 05:50
поделиться

Прошёл год с тех пор, как я разместил этот вопрос. После публикации я пару месяцев погрузился в Haskell. Мне это очень понравилось, но я отложил это в сторону, когда был готов погрузиться в монады. Я вернулся к работе и сосредоточился на технологиях, которые требовались моему проекту.

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

Из-за этого комментария я не только чувствую, что понимаю в точности , что такое монады… Я понимаю, что на самом деле написал некоторые вещи на C #, которые являются монадами… или по крайней мере очень близко, и стремятся решить те же проблемы.

Итак, вот комментарий - это прямая цитата из комментария здесь сильвана :

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

Так что позвольте мне подчиниться, и для большей ясности я сделаю пример на C #, даже если он будет выглядеть некрасиво. Я добавлю эквивалентный Haskell в конце и покажу вам крутой синтаксический сахар Haskell, в котором , IMO, монады действительно начинают приносить пользу.

Хорошо, одна из простейших монад называется "Может быть, монада" в Haskell. В C # тип Maybe называется Nullable . По сути, это крошечный класс, который просто инкапсулирует концепцию значения, которое либо допустимо и имеет значение, либо является "нулевым" и не имеет значения.

Полезно вставить в монаду для комбинирования значений этого типа понятие отказа. Т.е. мы хотим иметь возможность просматривать несколько значений, допускающих значение NULL, и возвращать null , как только любое из них становится нулевым. Это может быть полезно, если вы, например, ищете множество ключей в словаре или что-то в этом роде, и в конце вы хотите обработать все результаты и как-то их объединить, но если ни один из ключей отсутствует в словаре, вы хотите вернуть null для всего .Было бы утомительно вручную проверять каждый поиск для null и возврата, поэтому мы можем скрыть эту проверку внутри оператора привязки (который является своего рода точкой монады, мы скрываем бухгалтерский учет в операторе связывания, что упрощает использование кода, поскольку мы можем забыть о деталях).

Вот программа, которая мотивирует все это (я определю Bind позже, это просто для того, чтобы показать вам, почему это приятно).

  class Program 
 {
static Nullable  f () {return 4; } 
static Nullable  g () {return 7; } 
статический Nullable  h () {return 9; } 
 
 
static void Main (string [] args) 
 {
Nullable  z = 
f () .Bind (fval => 
g (). Bind (gval => 
h ()). Bind (hval => 
new Nullable  (fval + gval + hval )))); 
 
Console.WriteLine (
 "z = {0}", z.HasValue? Z.Value.ToString (): "null"); { {1}} Console.WriteLine ("Нажмите любую клавишу, чтобы продолжить ..."); 
Console.ReadKey (); 
} 
} 
 

Теперь проигнорируйте на мгновение, что это уже поддерживается для выполнения этого для Nullable в C # (вы можете добавить целые числа, допускающие значение NULL, и вы получите null, если любой из них имеет значение NULL ). Давайте представим, что такой возможности нет, и что это просто определяемый пользователем класс без особой магии. Дело в том, что мы можем использовать функцию Bind , чтобы привязать переменную к содержимому нашего Nullable значения, а затем сделать вид, что нет ничего странного , и используйте их как обычные целые числа и просто складывайте их вместе.Мы оборачиваем результат в конец, допускающий значение NULL, и этот допускающий значение NULL будет либо иметь значение NULL (если любое из f , g или ] h возвращает значение null), либо оно будет результатом суммирования f , g и h вместе. (это аналог того, как мы можем привязать строку в базе данных к переменной в LINQ и делать с ней что-нибудь, зная, что оператор Bind будет гарантировать, что в переменную будут передаваться только допустимые значения строки ).

Вы можете поиграть с этим и изменить любой из f , g h , чтобы вернуть null, и вы увидите, что все это вернет null.

Очевидно, что оператор связывания должен выполнить эту проверку за нас и вернуть значение null, если он встречает нулевое значение, а в противном случае передать по значению внутри Nullable в лямбду.

Вот оператор Bind :

 public static Nullable  Bind  (this Nullable  a, Func > f) {{ 1}} где B: struct 
, где A: struct 
 {
return a.HasValue? f (a.Value): null; 
} 
 

Типы здесь такие же, как в видео. Для этого требуется M a ( Nullable в синтаксисе C # для этого случая) и функция от a до {{1 }} M b ( Func > в синтаксисе C #) и возвращает M b ( Обнуляемый ).

Код просто проверяет, содержит ли обнуляемый объект значение, и если да, то извлекает его и передает в функцию, иначе он просто возвращает null. Это означает, что оператор Bind будет обрабатывать всю логику проверки на нуль за нас. Если и только если значение, которое мы вызываем Bind , не равно нулю, то это значение будет "передано" лямбда-функции , иначе мы выйдем из строя раньше и все выражение равно нулю.Это позволяет коду, который мы пишем с использованием монады, быть полностью свободным от этого поведения проверки нуля, мы просто используем Bind и получаем переменную, привязанную к значению внутри монадическое значение ( fval , gval и hval в примере кода), и мы можем безопасно использовать их в знание того, что Bind позаботится о том, чтобы проверить их на нуль, прежде чем передаст их.

Есть и другие примеры того, что вы можете делать с монадой. Для примера вы можете заставить оператор Bind заботиться о входном потоке символов и использовать его для написания комбинаторов синтаксического анализатора. Тогда каждый комбинатор синтаксического анализатора может полностью игнорировать такие вещи, как обратное отслеживание, сбои синтаксического анализатора и т. Д., И просто объединять меньшие синтаксические анализаторы вместе, как будто что-то никогда не пойдет не так, безопасно зная, что умная реализация Bind сортирует всю логику, стоящую за трудными битами. Позже, возможно, кто-то добавит протоколирование в монаду, , но код, использующий монаду, не изменится, потому что вся магия происходит в определении оператора Bind , остальная часть кода не изменилась.

Наконец, вот реализация того же кода в Haskell ( - начинается строкой комментария).

 - Вот тип данных, либо ничего, либо "Просто" значение 
 - это в стандартной библиотеке 
data. Может быть a = Nothing | Just a 
 
 - оператор связывания для Nothing 
Nothing >> = f = Nothing 
 - оператор связывания для Just x 
Just x >> = f = fx 
 
 - «единица», называемая «return» 
return = Just 
 
 - образец код с использованием лямбда-синтаксиса 
, который показал Брайан 
z = f >> = (\ fval -> 
g >> = (\ gval -> 
) h >> = (\ hval -> return (fval + gval + hval)))) 
 
 - следующее в точности то же, что и три строки выше 
z2 = do 
fval <- f 
gval <- g 
hval <- h 
return (fval + gval + hval) 
 

Как вы можете увидеть красивую нотацию do в конце, которая делает его похожим на прямой императивный код . И действительно, это сделано намеренно. Монады можно использовать для инкапсуляции всего полезного в императивном программировании (изменяемое состояние, ввод-вывод и т. Д.) И использовать этот красивый синтаксис, подобный императиву , но за кулисами , это всего лишь монады и умная реализация оператора связывания! Замечательно то, что вы можете реализовать свои собственные монады, реализовав >> = и return . И если вы это сделаете, эти монады также смогут использовать нотацию do , что означает, что вы можете писать на своих маленьких языках, просто определение двух функций!

43
ответ дан 23 November 2019 в 05:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: