Как инициализировать монаду, а затем использовать в функции много раз в Haskell - PullRequest
3 голосов
/ 19 апреля 2011

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

Ответы [ 2 ]

3 голосов
/ 19 апреля 2011

У меня проблемы с пониманием вашего вопроса.Также я не очень знаком с подсказкой.Но я попробую.

Насколько я могу судить, монада Interpreter - это просто простая оболочка состояния вокруг IO - она ​​существует только для того, чтобы вы могли сказать, например.setImportsQ [...] и последующие вычисления зависят от «настроек», которые были изменены этой функцией.Таким образом, вы хотите поделиться монадическим контекстом нескольких вычислений.Единственный способ сделать это - остаться в монаде - создать одно вычисление в Interpreter и запустить его один раз.Вы не можете иметь «глобальную переменную», которая экранирует и повторно использует runInterpreter.

К счастью, Interpreter является экземпляром MonadIO, что означает, что вы можете чередовать вычисления IO и Interpreterвычисления с использованием liftIO :: IO a -> Interpreter a.По сути, вы думаете наизнанку (крайне распространенная ошибка для учащихся Haskell).Вместо использования функции в IO, которая выполняет код в вашем интерпретаторе, используйте функцию в Interpreter, которая выполняет код в IO (а именно liftIO).Например,

main = runInterpreter $ do
    testHint
    expr1 <- liftIO getLine
    r1 <- interpret "" expr1 
    case r1 of
        ...
    expr2 <- liftIO getLine
    r2 <- interpret "" expr2
    case r2 of
        ...

И вы можете легко вывести этот последний код в функцию, если вам нужно, используя всю прелесть ссылочной прозрачности!Просто вытащите его прямо.

runSession :: Interpreter ()
runSession = do
    expr1 <- liftIO getLine
    r1 <- interpret "" expr1
    case interpret expr1 of
        ...

main = runInterpreter $ do
    testHint
    runSession

Имеет ли это смысл?Ваша вся программа является вычислением Interpreter, и только в последнюю минуту вы извлекаете ее в IO.

(что означает не , что означает, чтокаждая написанная вами функция должна быть в монаде Interpreter. Далеко от нее! Как обычно, используйте Interpreter по краям вашей программы и держите ядро ​​чисто функциональным. Interpreter - это новый IO).

3 голосов
/ 19 апреля 2011

Если я правильно понимаю, вы хотите инициализировать компилятор один раз и выполнять несколько запросов, возможно, в интерактивном режиме.

Существует два основных подхода:

  • lift IO actionsв ваш Interpreter контекст (см. ответ luqui).
  • используйте ленивый ввод-вывод для перемещения потока данных в вашу программу и из нее.

Я опишу второй вариант.


С помощью ленивого ввода-вывода вы можете передать testHint ленивый поток ввода, а затем выполнить цикл в теле testHint, интерактивно интерпретируя многие запросы:

main = do
      ls <- getContents   -- a stream of future input
      r <- runInterpreter (testHint (lines input))
      case r of
         Left err -> printInterpreterError err
         Right () -> putStrLn "Done."

testHint input = do
      loadModules ["src/Test/SomeModule.hs"]
      setImportsQ [("Prelude", Nothing), ("Test.SomeModule", Just "SM")]
      say "loaded"

      -- loop over the stream of input, interpreting commands
      let go (":quit":es) = return ()
             (e:es)       = do say =<< typeOf e
                               go es
      go

Функция go имеет доступ к закрытой среде инициализированного интерпретатора, поэтому подача ей событий, очевидно, будет выполняться в области действия этого инициализированного интерпретатора.

Альтернативным методом будетизвлечь состояние интерпретатора из монады, но я не уверен, что это возможно в GHC (это потребовало бы, чтобы GHC не входил в монаду IO в принципе).

...