将StateT与InputT结合起来 [英] combining StateT with InputT
问题描述
这是此问题的后续行动。我试图在@ 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
无 - >提升$ outputStrLn再见。
只需输入 - > (liftIO $ process c input)>>循环
getLineIO ::(MonadException m)=> IO字符串 - > InputT m(Maybe String)
getLineIO ios = do
s< - liftIO ios
getInputLine s
获取错误
Main.hs:59:10:
Couldn' 'Input'与'IO'的匹配类型
预期类型:StateT [String] IO()
实际类型:StateT [String](InputT m0)()
相关绑定包括
loop :: InputT(InputT m0)()(在Main.hs:61:3处绑定)
在表达式中:lift $ runInputT defaultSettings loop
在'repl'等式中:
repl c
= lift $ runInputT defaultSettings loop
where
loop
= do {minput< - getLineIO $ in_ps1 $ c;
....}
Main.hs:62:5:
由do语句引起的(Monad m0)没有实例
类型变量'm0 '不明确
相关绑定包括
loop :: InputT(InputT m0)()(绑定在Main.hs:61:3)
注意:有几个潜在的实例:
实例Monad(Text.Parsec.Prim.ParsecT sum)
- 在'Text.Parsec.Prim'中定义
实例Monad(或e) - 在'Data.E'中定义
实例Monad Data.Proxy.Proxy - 在'Data.Proxy'中定义
...另外15个
在'do'块的结尾处:minput< - getLineIO $ in_ps1 $ c
在表达式中:
do {minput< - getLineIO $ in_ps1 $ c;
的情况下输入为{
Nothing - >提升$ outputStrLn再见。
只需输入 - > (liftIO $ process c input)>>循环}}
在'循环'的等式中:
循环
= do {minput< - getLineIO $ in_ps1 $ c;
的情况下输入为{
Nothing - >提升$ outputStrLn再见。
只需输入 - > (liftIO $ process c input)>>循环}}
我知道 haskelline
内置了对历史的支持,但我试图自己实现它一个练习。
随意为monad变换器提供相同功能的替换。
我的真正问题
我想为lambda REPL添加 ipython
给你写一个Haskell,即:
I。输入和输出计数器,将出现在提示符中,即
In [1]>
出[1]>
这已经是 done 。
将每个命令保存到历史记录(自动),并使用特殊命令显示所有先前的命令,例如 histInput
(与 hist 在
ipython
中相同))。另外,保存所有输出结果的历史记录,并使用 histOutput
显示它们。这是我在这个问题上所要做的(只是暂时输入历史记录)。
三。参考先前的输入和输出,例如如果 In [1]
为 x
,则 In [1] + 2
应该被 x + 2
取代,输出也是如此。
更新
我试图合并@ ErikR的answer ,并临时禁用 showStep
,并提供:
模块Main其中
import语法
导入解析器
导入评估
import Pretty
导入计数器
导入Control.Monad
导入Control.Monad.Trans
导入System.Console.Haskeline
导入Control.Monad.State
showStep ::(Int,Expr) - > IO()
showStep(d,x)= putStrLn((replicate d'')++=>++ ppexpr x)
process :: Counter - >字符串 - > InputT(StateT [String] IO)()
process c line =
if((length line)> 0)
then
if(head line)/ ='% '
然后做
修改(++ [line])
let res = parseExpr行
案例res
Left err - > outputStrLn $ show err
右前 - > 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:不要为空行增加计数器
else do
outputStrLn
out2iout: :字符串 - > IO字符串
out2iout s =返回s
out_ps1 ::计数器 - > IO字符串 - > 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字符串
handle_cmd行= if行==%hist
然后
evalStateT getHist []
else
返回未知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
Nothing - > return()
只需输入 - >处理c输入>>循环c
getLineIO ::(MonadException m)=> IO字符串 - > InputT m(Maybe String)
getLineIO ios = do
s < - liftIO ios
getInputLine s
in_ps1 :: Counter - > IO字符串
in_ps1 c = do
let ion = c 1
n < - ion
let s =Untyped:In [++(show n)++] >
return s
仍然无法编译:
Main.hs:59:5:
无法将类型'[]'与'StateT [String] IO'
预期类型:StateT [String] IO字符串
实际类型:[()]
在'do'块的标记中:
forM_(zip [(1 :: Int) ..] hist)
$ \(i,h) - > do {show i ++:++ show h}
在表达式中:
do {hist< - lift get;
forM_(zip [(1 :: Int)..] hist)$ \(i,h) - > do {...}}
在'getHist'的等式中:
getHist
= do {hist< - lift get;
forM_(zip [(1 :: Int)..] hist)$ \(i,h) - > ...}
猜测你在做什么。
这个程序可以识别下列命令:
hist - 显示当前历史记录
添加xxx - 将xxx添加到历史记录列表
清除 - 清除历史记录列表
count - 显示历史记录的数量
quit - 退出命令循环
程序源代码:
import System.Console.Haskeline
import Control.Monad.Trans.Class
import Control.Monad.Trans.State.Strict
import Control.Monad
$ b $ main :: IO()
main = evalStateT(runInputT defaultSettings loop)[]
loop :: InputT(StateT [String ] IO)()
loop = do
minput< - getInputLine%
case
Nothing - > return()
只是退出 - > return()
只需输入 - >过程输入>>循环
过程输入=做
让args =字输入
案例参数
[] - > return()
(hist:_) - > showHistory
(add:x:_) - > lift $ modify(++ [x])
(clear:_) - > lift $ modify(const [])
(count:_) - > do hs< - lift get
outputStrLn $历史项目数量:++ show(length hs)
_ - > outputStrLn???
$ b showHistory = do
hist< - lift得到
forM_(zip [(1 :: Int)..] hist)$ \(i,h) - > ;做
outputStrLn $ show i ++++ h
It is a follow-up to this question. I'm trying to combine shell
from @ErikR's answer in my InputT
loop.
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
And getting an error
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 } }
The full code can be found here, it's based on Write you a haskell.
I know haskelline
has a built-in support for history, but I'm trying to implement it myself as an exercise.
Feel free to suggest replacements for the monad transformers to get the same functionality.
My Real Problem
I'd like to add ipython
like capabilities to the lambda REPL in Write You a Haskell, namely:
I. A counter for input and output, that will appear in the prompt, i.e
In[1]>
Out[1]>
This is already done.
II. Save each command to history (automatically), and display all previous commands using a special command, e.g. histInput
(same as hist
in ipython
). Also, save a history of all output results and display them using histOutput
. This is what I'm trying to do in this question (input history only for the moment).
III. Reference previous inputs and outputs, e.g. if In[1]
was x
, then In[1] + 2
should be substituted by x + 2
, and likewise for the output.
Update
I've tried to combine @ErikR's answer, and temporarily disabled showStep
, coming up with:
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
which still doesn't compile:
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) -> ... }
I'm going to take a guess at what you are trying to do.
This program recognizes the following commands:
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
Program source:
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
这篇关于将StateT与InputT结合起来的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!