Я являюсь новым в haskell, я должен записать программу, с учетом контекста, таким образом, я думал, что могу использовать Монаду Читателя для хранения контекста, считанного из файла, я знаю, как считать файл, поместив содержание в список tuplessomething как [([Символ], [Символ])], но я не знаю, как реализовать Монаду Читателя для предоставления доступа к среде, для всех компонентов моей программы, не используя обязательный стиль, В особенности я не знаю, как установить и использовать среду, насколько я понял, что должен дать его как параметр ко всем функциям, для которых нужна среда с ENV функции runReader, но я очень смущен, кто-то может дать мне некоторые признаки или хорошее учебное руководство?заранее спасибо
Я думаю, что проще всего, если вы посмотрите, как можно решить эту проблему без использования Reader, а затем сравните переведенную версию. Вот урезанный пример из программы, над которой я работаю, где среда представляет собой набор функций обратного вызова для обновления дисплея. Это немного сложнее, потому что он использует ReaderT вместо Reader, но все работает в основном так же.
runProcess :: Env -> State -> Action -> IO State
runProcess env state action = do
newstate <- processAction state action
let ufunc = mainUFunc env -- get the callback to update the display
ufunc newstate -- update the display
return newstate
Теперь я изменю его, чтобы использовать монаду Reader для передачи окружения. Поскольку код уже был в IO, необходимо использовать версию преобразователя монад, ReaderT
.
runProcessR :: State -> Action -> ReaderT Env IO State
runProcessR state action = do
newstate <- lift $ processAction state action
env <- ask -- get the environment from the reader
liftIO $ (mainUFunc env) newstate -- updating is in IO; it needs to be lifted
return newstate
На этом этапе основной цикл программы будет выглядеть следующим образом:
loop :: State -> ReaderT Env IO ()
loop = do
action <- liftIO getAction
if action == EndLoop
then return ()
else do
st' <- processActionR st action
loop st'
mainLoop :: IO ()
mainLoop = do
env <- setUpCallbacks
let st = initState
runReaderT $ loop st
Вот как вы можете использовать Reader. Каждая функция, которая раньше принимала параметр среды, больше не нуждается в этом.Функции, не использующие среду, можно использовать напрямую или поднимать, если они монадические.
Базовая схема использования любой «нормальной» монады [0] практически одинакова для всех. По сути:
do
, если хотите, точно так же, как если бы вы написали функцию IO
, например main
]. <-
, чтобы получить значение «внутри», вызывая «выполнение» другого значения. let
, оставив его независимым от монадической структуры. Сделайте это, и все беспорядочные детали дополнительных функций, описываемых монадой (в данном случае, передача дополнительного параметра среды), обрабатываются автоматически.
Обычными операциями Reader являются ask
и local
:
ask
- монадическое значение, содержащее окружение; в блоке do
вы используете его так же, как вы использовали бы что-то вроде getLine
в монаде IO
. local
принимает функцию, которая предоставляет новую среду и вычисления в монаде Reader, запускает последнюю в среде, измененной первой, затем берет результат и помещает его в текущую функцию. Другими словами, он выполняет подсчет в локально измененной среде. Функция "run" - это функция с творческим названием runReader
, которая просто берет вычисление в монаде Reader и значение среды, запускает первое, используя второе, и возвращает окончательный результат за пределами монада.
В качестве примера, вот некоторые функции, выполняющие бессмысленные вычисления в монаде Reader, где окружение - это «максимальное значение», указывающее, когда следует остановиться:
import Control.Monad.Reader
computeUpToMax :: (Int -> Int) -> Int -> Reader Int [Maybe Int]
computeUpToMax f x = do
maxVal <- ask
let y = f x
if y > maxVal
then return []
else do zs <- local (subtract y) (computeUpToMax f y)
z <- frob y
return (z:zs)
frob :: Int -> Reader Int (Maybe Int)
frob y = do
maxVal <- ask
let z = maxVal - y
if z == y
then return Nothing
else return $ Just z
Чтобы запустить его, вы должны использовать что-то вроде этого:
> runReader (computeUpToMax (+ 1) 0) 9
[Just 8, Just 6, Nothing]
... где 9
- начальная среда.
Почти такая же структура может использоваться с другими монадами, такими как Состояние
, Может быть
или []
, хотя в последних двух случаях вы обычно просто используете конечное монадическое значение результата вместо использования функции "run".
[0]: где нормальный означает отсутствие использования магии компилятора, наиболее очевидной «ненормальной» монадой, конечно же, является IO
.
IMHO лучший ресурс монад - All About Monads , а вот часть для Reader monad .