Это последующий контроль за this question. Я пытаюсь совместить shell
от @ ErikR's answer в моей петле InputT
.Объединение состоянияT с 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
имеет встроенную поддержку истории, но я пытаюсь реализовать ее сам как упражнение.
Не стесняйтесь предлагать замену для монадных трансформаторов для получения одинаковой функциональности.
Моя реальная проблема
Я хотел бы добавить ipython
как возможности для лямбда РЕПЛ в выпишу вам Haskell, а именно:
I. Счетчик для ввода и вывода, которые будут появляться в строке, т.е.
In[1]>
Out[1]>
Это уже done.
II. Сохраните каждую команду в истории (автоматически) и отобразите все предыдущие команды с помощью специальной команды, например. histInput
(такой же, как hist
в ipython
). Кроме того, сохраните историю всех результатов вывода и покажите их с помощью histOutput
. Это то, что я пытаюсь сделать в этом вопросе (входная история только на данный момент).
III. Ссылка на предыдущие входы и выходы, например. если In[1]
был x
, то In[1] + 2
следует заменить на x + 2
, а также для вывода.
Update
Я пытался объединить @ ErikR-х answer, и временно отключить showStep
, придумывая:
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) -> ... }
haskeline уже реализует историю команд линии для вас - посмотрите на [пример использования] (https://hackage.haskell.org/ package/haskeline-0.7.2.3/docs/System-Console-Haskeline.html) – ErikR
Спасибо, я знаю, но я хочу реализовать его сам как упражнение. – dimid
Если вы хотите сами внедрить историю, то почему в вашем коде появляется 'InputT'? – ErikR