作为一项练习,我正在用Haskell编写一个命令行逆波兰计算器。它的思路是提示输入(数字或运算符),并打印出新的堆栈。我的计划是将数字列表存储在状态单子上,并针对该列表执行运算。例如:
> 4
[4]
> 3
[3,2]
> 5
[5,3,2]
> +
[8, 2]
等等。
我首先尝试使用状态单子来在每个条目上进行输入和输出,以构建列表。由于同一函数中包含IO和State的组合,我已经陷入了困境。我的问题是,我还需要在输入上进行递归,以保持提示在输入第一个数字后继续。
到目前为止,这是我的代码:
module Main where
import Control.Monad.State
addEntry :: Int -> State [Int] Int
addEntry entry = do
entries <- get
put (entry : entries)
return entry
mainLoop :: [Int] -> IO ()
mainLoop entries = do
entry <- readLn
newEntries <- execState (addEntry entry) entries
print newEntries
mainLoop newEntries
main :: IO ()
main = do
print $ mainLoop []
这是我当前遇到的编译错误:
src/Main.hs@14:28-14:42 Couldn't match type [Int] with ‘IO [Int]’
Expected type: State (IO [Int]) Int
Actual type: State [Int] Int …
src/Main.hs@14:44-14:51 Couldn't match expected type ‘IO [Int]’ with actual type [Int] …
有没有关于如何构建这些函数的建议,使我不需要结合IO和State?
StateT [Int] IO Int
替代StateT [Int] Identity Int
(它与State [Int] Int
相同)?这样,你就可以使用liftIO
将 IO 操作提升到StateT [Int] IO
单子中,在同一个 do-block 中执行有状态的操作和 IO 操作。 - bheklilr{-# LANGUAGE FlexibleContexts #-}
,然后将addEntry
的类型签名更改为MonadState [Int] m => Int -> m Int
,然后将execState
更改为execStateT
,则可以使其编译通过,但是您还会遇到一个编译器错误,即print $ mainLoop []
应该只是mainLoop []
,因为它本身就会打印并且不返回任何内容。像这样 - bheklilrStateT MyState IO
。 - bheklilrMonadState
上下文使这些函数更容易组合,并且它将所有IO操作都保留在rpnREPL
内部。 - bheklilr