справка с монадой читателя

Я являюсь новым в haskell, я должен записать программу, с учетом контекста, таким образом, я думал, что могу использовать Монаду Читателя для хранения контекста, считанного из файла, я знаю, как считать файл, поместив содержание в список tuplessomething как [([Символ], [Символ])], но я не знаю, как реализовать Монаду Читателя для предоставления доступа к среде, для всех компонентов моей программы, не используя обязательный стиль, В особенности я не знаю, как установить и использовать среду, насколько я понял, что должен дать его как параметр ко всем функциям, для которых нужна среда с ENV функции runReader, но я очень смущен, кто-то может дать мне некоторые признаки или хорошее учебное руководство?заранее спасибо

13
задан arpho 10 August 2010 в 17:11
поделиться

3 ответа

Я думаю, что проще всего, если вы посмотрите, как можно решить эту проблему без использования 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. Каждая функция, которая раньше принимала параметр среды, больше не нуждается в этом.Функции, не использующие среду, можно использовать напрямую или поднимать, если они монадические.

5
ответ дан 2 December 2019 в 00:30
поделиться

Базовая схема использования любой «нормальной» монады [0] практически одинакова для всех. По сути:

  • Напишите функции, возвращающие значение монадического типа, используя нотацию do , если хотите, точно так же, как если бы вы написали функцию IO , например main ].
  • Используйте любые специальные функции для монады, с которой вы работаете.
  • Вызов этих функций друг от друга, используя стандартное правило:
    • Свяжите значение из той же монады, используя <- , чтобы получить значение «внутри», вызывая «выполнение» другого значения.
    • Свяжите любое другое значение с помощью let , оставив его независимым от монадической структуры.
  • Используйте специальную функцию «run» конкретной монады, чтобы оценить монадическое вычисление и получить окончательный результат.

Сделайте это, и все беспорядочные детали дополнительных функций, описываемых монадой (в данном случае, передача дополнительного параметра среды), обрабатываются автоматически.

Обычными операциями 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 .

8
ответ дан 2 December 2019 в 00:30
поделиться

IMHO лучший ресурс монад - All About Monads , а вот часть для Reader monad .

1
ответ дан 2 December 2019 в 00:30
поделиться
Другие вопросы по тегам:

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