Читательская монада настолько сложна и кажется бесполезной. В императивном языке, таком как Java или C ++, нет эквивалентной концепции для читающей монады, если я не ошибаюсь.
Вы можете привести мне простой пример и немного прояснить это?
haskell
monads
reader-monad
chipbk10
источник
источник
Ответы:
Не бойтесь! Монада чтения на самом деле не так уж сложна и имеет очень простую в использовании утилиту.
Есть два способа приблизиться к монаде: мы можем спросить
С первого подхода монада читателя - это некий абстрактный тип
такой, что
Итак, как нам это использовать? Что ж, монада читателя хороша для передачи (неявной) информации о конфигурации посредством вычислений.
Каждый раз, когда у вас есть «константа» в вычислении, которая вам нужна в различных точках, но на самом деле вы хотите иметь возможность выполнять одно и то же вычисление с разными значениями, тогда вам следует использовать монаду чтения.
Монады чтения также используются для того, что в объектно-ориентированном стиле называют внедрением зависимостей . Например, алгоритм негамакса часто используется (в высокооптимизированных формах) для вычисления значения позиции в игре для двух игроков. Однако сам алгоритм не заботится о том, в какую игру вы играете, за исключением того, что вам нужно иметь возможность определять, какие «следующие» позиции находятся в игре, и вам нужно быть в состоянии определить, является ли текущая позиция победной.
Тогда это будет работать с любой конечной детерминированной игрой для двух игроков.
Этот шаблон полезен даже для вещей, которые на самом деле не являются внедрением зависимостей. Предположим, вы работаете в сфере финансов, вы можете разработать сложную логику для ценообразования актива (скажем, производного инструмента), что хорошо, и вы можете обойтись без каких-либо вонючих монад. Но затем вы изменяете свою программу, чтобы работать с несколькими валютами. Вам нужно иметь возможность конвертировать между валютами на лету. Ваша первая попытка - определить функцию верхнего уровня
получить спотовые цены. Затем вы можете вызвать этот словарь в своем коде ... но подождите! Это не сработает! Валютный словарь неизменен и поэтому должен быть одинаковым не только на протяжении всей жизни вашей программы, но и с момента ее компиляции ! Ну так что ты делаешь? Ну, один из вариантов - использовать монаду Reader:
Возможно, самый классический вариант использования - реализация интерпретаторов. Но прежде чем мы посмотрим на это, нам нужно ввести еще одну функцию
Итак, Haskell и другие функциональные языки основаны на лямбда-исчислении . Синтаксис лямбда-исчисления выглядит так:
и мы хотим написать оценщик для этого языка. Для этого нам нужно будет отслеживать среду, которая представляет собой список привязок, связанных с терминами (на самом деле это будут замыкания, потому что мы хотим сделать статическую область видимости).
Когда мы закончим, мы должны получить значение (или ошибку):
Итак, напишем интерпретатор:
Наконец, мы можем использовать его, передав тривиальное окружение:
Вот и все. Полнофункциональный интерпретатор лямбда-исчисления.
Другой способ подумать об этом - спросить: как это реализовано? Ответ состоит в том, что монада чтения на самом деле одна из самых простых и элегантных из всех монад.
Reader - это просто причудливое название для функций! Мы уже определились,
runReader
а как насчет других частей API? Ну, каждыйMonad
- это еще иFunctor
:Теперь, чтобы получить монаду:
что не так уж и страшно.
ask
действительно просто:пока
local
не так уж и плохо:Итак, монада чтения - это просто функция. Зачем вообще нужен Reader? Хороший вопрос. Собственно, вам это и не нужно!
Это еще проще. Более того,
ask
это простоid
иlocal
просто композиция функций с порядком функций переключились!источник
Reader
есть функция с какой-то конкретной реализацией класса типа монады? Сказать это раньше помогло бы мне немного меньше озадачиться. Сначала я этого не понимал. На полпути я подумал: «О, это позволяет вам вернуть что-то, что даст вам желаемый результат, если вы укажете недостающее значение». Я подумал, что это полезно, но внезапно понял, что функция делает именно это.local
функция требует дополнительных пояснений ..(Reader f) >>= g = (g (f x))
?x
?Помню, я был озадачен, как и вы, пока сам не обнаружил, что варианты монады Читателя встречаются повсюду . Как я это обнаружил? Потому что я продолжал писать код, который оказался небольшими вариациями.
Например, однажды я писал код для работы с историческими значениями; ценности, которые меняются со временем. Очень простая модель этого - функции от моментов времени до значения в этот момент времени:
В
Applicative
случае означает , что если у вас естьemployees :: History Day [Person]
иcustomers :: History Day [Person]
вы можете сделать это:Т.е.
Functor
иApplicative
позволяют адаптировать обычные, неисторические функции для работы с историями.Пример монады наиболее интуитивно понятен при рассмотрении функции
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
. Функция типаa -> History t b
- это функция, которая отображаетa
в историюb
значений; например, у вас могло бытьgetSupervisor :: Person -> History Day Supervisor
иgetVP :: Supervisor -> History Day VP
. Таким образом, экземпляр Monad дляHistory
создания таких функций; например,getSupervisor >=> getVP :: Person -> History Day VP
это функция, которая для любого типа получаетPerson
историюVP
s, которая у них была.Ну, это
History
монада на самом деле точно так же , какReader
.History t a
действительно то же самое, чтоReader t a
(что то же самое, чтоt -> a
).Другой пример: недавно я создавал прототипы OLAP- проектов на Haskell. Одна из идей здесь - это идея «гиперкуба», который представляет собой отображение пересечений набора измерений на значения. Это снова мы:
Одна из распространенных операций с гиперкубами - применение многомерных скалярных функций к соответствующим точкам гиперкуба. Этого можно добиться, определив
Applicative
экземпляр дляHypercube
:Я просто скопировал приведенный
History
выше код и изменил имена. Как видите,Hypercube
тоже справедливоReader
.Это продолжается и продолжается. Например, переводчики языка также сводятся к следующему
Reader
, когда вы применяете эту модель:Reader
ask
Reader
среда выполнения.local
Хорошая аналогия: a
Reader r a
представляет собойa
«дыры» в нем, которые мешают вам понять, о чемa
мы говорим. Вы можете получить реальный результатa
только после того, какr
заполните дыры. Таких вещей масса. В приведенных выше примерах «история» - это значение, которое нельзя вычислить, пока вы не укажете время, гиперкуб - это значение, которое невозможно вычислить, пока вы не укажете пересечение, а выражение языка - это значение, которое может не будут вычисляться, пока вы не укажете значения переменных. Это также дает вам интуитивное представление о том, почемуReader r a
это то же самоеr -> a
, потому что такая функция также интуитивно являетсяa
отсутствующимr
.Таким образом
Functor
, экземпляры ,Applicative
и являются очень полезным обобщением для случаев, когда вы моделируете что-либо типа « что-то отсутствует », и позволяют вам рассматривать эти «неполные» объекты, как если бы они были завершенными.Monad
Reader
a
r
Еще один способ сказать то же самое: а
Reader r a
что - то , что потребляетr
и производитa
, иFunctor
,Applicative
иMonad
экземпляры являются основными формами работы сReader
с.Functor
= сделать,Reader
что изменяет вывод другогоReader
;Applicative
= подключить дваReader
s к одному входу и объединить их выходы;Monad
= проверить результат aReader
и использовать его для построения другогоReader
. Функцииlocal
иwithReader
= создают,Reader
который изменяет ввод на другойReader
.источник
GeneralizedNewtypeDeriving
расширение для выводаFunctor
,Applicative
,Monad
и т.д. для ньютайпов на основе базовых их типов.В Java или C ++ вы можете без проблем получить доступ к любой переменной из любого места. Проблемы возникают, когда ваш код становится многопоточным.
В Haskell у вас есть только два способа передать значение от одной функции к другой:
fn1 -> fn2 -> fn3
функцияfn2
может не понадобиться параметр , который вы передаете отfn1
доfn3
.Монада Reader просто передает данные, которые вы хотите разделить между функциями. Функции могут читать эти данные, но не могут их изменить. Это все, чем занимается монада Читателя. Ну почти все. Также есть ряд функций вроде
local
, но в первый раз можно придерживатьсяasks
только.источник
do
нотации, который лучше было бы преобразовать в чистую функцию.where
предложения, будет ли это принято как третий способ передачи переменных?