将StateT与InputT

时间:2016-06-20 19:19:14

标签: haskell monads monad-transformers state-monad

这是this question的后续行动。我正在尝试将@ {ErikR的answer shell合并到我的InputT循环中。

main :: IO [String]
main = do
    c <- makeCounter
    execStateT (repl c) []

repl :: Counter -> StateT [String] IO ()
repl c = lift $ runInputT defaultSettings loop
  where
  loop = do
    minput <- getLineIO $ in_ps1 $ c
    case minput of
      Nothing -> lift $ outputStrLn "Goodbye."
      Just input -> (liftIO $ process c input) >> loop

getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String)
getLineIO ios = do
    s <- liftIO ios
    getInputLine s

收到错误

Main.hs:59:10:
    Couldn't match type ‘InputT m0’ with ‘IO’
    Expected type: StateT [String] IO ()
      Actual type: StateT [String] (InputT m0) ()
    Relevant bindings include
      loop :: InputT (InputT m0) () (bound at Main.hs:61:3)
    In the expression: lift $ runInputT defaultSettings loop
    In an equation for ‘repl’:
        repl c
          = lift $ runInputT defaultSettings loop
          where
              loop
                = do { minput <- getLineIO $ in_ps1 $ c;
                       .... }

Main.hs:62:5:
No instance for (Monad m0) arising from a do statement
The type variable ‘m0’ is ambiguous
Relevant bindings include
  loop :: InputT (InputT m0) () (bound at Main.hs:61:3)
Note: there are several potential instances:
  instance Monad (Text.Parsec.Prim.ParsecT s u m)
    -- Defined in ‘Text.Parsec.Prim’
  instance Monad (Either e) -- Defined in ‘Data.Either’
  instance Monad Data.Proxy.Proxy -- Defined in ‘Data.Proxy’
  ...plus 15 others
In a stmt of a 'do' block: minput <- getLineIO $ in_ps1 $ c
In the expression:
  do { minput <- getLineIO $ in_ps1 $ c;
       case minput of {
         Nothing -> lift $ outputStrLn "Goodbye."
         Just input -> (liftIO $ process c input) >> loop } }
In an equation for ‘loop’:
    loop
      = do { minput <- getLineIO $ in_ps1 $ c;
             case minput of {
               Nothing -> lift $ outputStrLn "Goodbye."
               Just input -> (liftIO $ process c input) >> loop } }

可以找到完整的代码here,它基于Write you a haskell

我知道haskelline内置了对历史的支持,但我正在努力将其作为练习来实现。

随意建议替换monad变换器以获得相同的功能。

我的真实问题

我想在Write You a Haskell中将ipython类似功能添加到lambda REPL中,即:

予。输入和输出的计数器,将出现在提示中,即

In[1]>
Out[1]>

这已经是done

II。将每个命令保存到历史记录(自动),并使用特殊命令显示所有先前的命令,例如histInput(与hist中的ipython相同)。此外,保存所有输出结果的历史记录并使用histOutput显示它们。这就是我在这个问题上要做的事情(目前仅输入历史记录)。

III。参考先前的输入和输出,例如如果In[1]x,则In[1] + 2应替换为x + 2,同样也应替换为showStep

更新

我试图将@ ErikR的answer与暂时停用module Main where import Syntax import Parser import Eval import Pretty import Counter import Control.Monad import Control.Monad.Trans import System.Console.Haskeline import Control.Monad.State showStep :: (Int, Expr) -> IO () showStep (d, x) = putStrLn ((replicate d ' ') ++ "=> " ++ ppexpr x) process :: Counter -> String -> InputT (StateT [String] IO) () process c line = if ((length line) > 0) then if (head line) /= '%' then do modify (++ [line]) let res = parseExpr line case res of Left err -> outputStrLn $ show err Right ex -> do let (out, ~steps) = runEval ex --mapM_ showStep steps out_ps1 c $ out2iout $ show out else do let iout = handle_cmd line out_ps1 c iout -- TODO: don't increment counter for empty lines else do outputStrLn "" out2iout :: String -> IO String out2iout s = return s out_ps1 :: Counter -> IO String -> InputT (StateT [String] IO) () out_ps1 c iout = do out <- liftIO iout let out_count = c 0 outputStrLn $ "Out[" ++ (show out_count) ++ "]: " ++ out outputStrLn "" handle_cmd :: String -> IO String handle_cmd line = if line == "%hist" then evalStateT getHist [] else return "unknown cmd" getHist :: StateT [String] IO String getHist = do hist <- lift get forM_ (zip [(1::Int)..] hist) $ \(i, h) -> do show i ++ ": " ++ show h main :: IO () main = do c <- makeCounter repl c repl :: Counter -> IO () repl c = evalStateT (runInputT defaultSettings(loop c)) [] loop :: Counter -> InputT (StateT [String] IO) () loop c = do minput <- getLineIO $ in_ps1 $ c case minput of Nothing -> return () Just input -> process c input >> loop c getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String) getLineIO ios = do s <- liftIO ios getInputLine s in_ps1 :: Counter -> IO String in_ps1 c = do let ion = c 1 n <- ion let s = "Untyped: In[" ++ (show n) ++ "]> " return s 结合起来,提出:

Main.hs:59:5:
    Couldn't match type ‘[]’ with ‘StateT [String] IO’
    Expected type: StateT [String] IO String
      Actual type: [()]
    In a stmt of a 'do' block:
      forM_ (zip [(1 :: Int) .. ] hist)
      $ \ (i, h) -> do { show i ++ ": " ++ show h }
    In the expression:
      do { hist <- lift get;
           forM_ (zip [(1 :: Int) .. ] hist) $ \ (i, h) -> do { ... } }
    In an equation for ‘getHist’:
        getHist
          = do { hist <- lift get;
                 forM_ (zip [(1 :: Int) .. ] hist) $ \ (i, h) -> ... }

仍然无法编译:

access_log

3 个答案:

答案 0 :(得分:1)

第一个错误是因为你声明了

main :: IO ()

但也

execStateT (...) :: IO [String]

execStateT返回计算的最终状态,您的状态为[String]。通常,这可以通过不为main声明类型并将其推断为某些IO a的{​​{1}}来解决。第二个我不确定,但也许它是一样的。

答案 1 :(得分:1)

我会猜测你想要做什么。

该程序识别以下命令:

hist        -- show current history
add xxx     -- add xxx to the history list
clear       -- clear the history list
count       -- show the count of history items
quit        -- quit the command loop

节目来源:

import System.Console.Haskeline
import Control.Monad.Trans.Class
import Control.Monad.Trans.State.Strict
import Control.Monad

main :: IO ()
main = evalStateT (runInputT defaultSettings loop) []

loop :: InputT (StateT [String] IO) ()
loop = do
  minput <- getInputLine "% "
  case minput of
      Nothing -> return ()
      Just "quit" -> return ()
      Just input -> process input >> loop

process input = do
  let args = words input
  case args of
    []  -> return ()
    ("hist": _)     -> showHistory
    ("add" : x : _) -> lift $ modify (++ [x]) 
    ("clear": _)    -> lift $ modify (const [])
    ("count": _)    -> do hs <- lift get
                          outputStrLn $ "number of history items: " ++ show (length hs)
    _               -> outputStrLn "???"

showHistory = do
  hist <- lift get
  forM_ (zip [(1::Int)..] hist) $ \(i,h) -> do
    outputStrLn $ show i ++ " " ++ h

答案 2 :(得分:0)

您编写的代码here,并将process定义为:

process :: Counter -> String -> IO ()

使用此签名创建process版本:

Counter -> String -> InputT (StateT [String] IO) ()

只需使用liftIO:

process' :: Counter -> String -> InputT (StateT [String] IO) ()
process' counter str = liftIO $ process counter str