我想编写一个文本接口,它提供了一些默认命令。此程序支持这些命令的选项卡完成。
该程序还记录用户输入并将其存储在StateData中。现在我希望这个程序支持那些用户输入的选项卡完成。例如:
*Main > main
> read a<tab> -- press tab and no suggestions (read is a default command)
> read abcde
...
> read a<tab> -- press tab
abcde -- suggestions在不使用像IORef这样的不安全机制的情况下能够做到这一点吗?有没有一种方法可以将更新的st从loop (在repl中)传递给replSettings startState (在repl中)?
我是新来的Haskeline,谢谢您的时间。
repl :: StateData -> IO()
repl startState = runInputT (replSettings startState) $ loop startState
where
loop :: StateData -> InputT IO ()
loop st = do
inputL <- getInputLine "> "
case inputL of
Nothing -> return ()
Just "quit" -> outputStrLn "--Exited--" >> return ()
Just ipt -> do (opt, st') <- process ipt `runStateT` st
...
loop st'
replSettings :: StateData -> Settings IO
replSettings st =
Settings
{ complete = replCompletion st,
historyFile = Just "history.txt",
autoAddHistory = True
}
replCompletion :: StateData -> CompletionFunc IO
replCompletion st = completeWordWithPrev Nothing [' '] st (\x y -> return $ completionGenerator x y)
completionGenerator :: String -> String -> StateData -> [Completion]
completionGenerator "" c st =
commandSuggestion c (updatesSuggestions st) -- I wish to update it at run time
completionGenerator p c st = ...发布于 2021-03-29 19:31:20
IORef并不不安全;您已经进入了IO,因此在这里添加可变状态是非常合理的方法。
但是,如果您想要避免IO,可以简单地使用StateT StateData IO作为InputT的底层monad,从而在Settings中使用完成函数。看来您已经在尝试使用StateT了。下面是一个完整的示例,它只是将每个条目添加到列表中,然后天真地自动完成它们:
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State (StateT, evalStateT, get, modify)
import Data.List (isPrefixOf)
import System.Console.Haskeline
type StateData = [String]
main :: IO ()
main = repl []
repl :: StateData -> IO ()
repl startState
= flip evalStateT startState
$ runInputT replSettings loop
where
loop :: InputT (StateT StateData IO) ()
loop = do
inputL <- getInputLine "> "
case inputL of
Nothing -> pure ()
Just "quit" -> outputStrLn "--Exited--"
Just ipt -> do
-- Just add each entry to the state directly.
lift $ modify (ipt :)
loop
replSettings :: Settings (StateT StateData IO)
replSettings = Settings
{ complete = replCompletion
, historyFile = Just "history.txt"
, autoAddHistory = True
}
replCompletion :: CompletionFunc (StateT StateData IO)
replCompletion = completeWordWithPrev Nothing " " completionGenerator
completionGenerator :: String -> String -> StateT StateData IO [Completion]
completionGenerator prefix suffix = do
st <- get
-- Trivial completion that just ignores the suffix.
pure $ fmap (\ s -> Completion s s True)
$ filter (prefix `isPrefixOf`) st还可以使用MonadState (来自mtl)编写完成生成器,以防止它访问IO,其他代码也可以使用这种纯状态,而对IO不知情。但否则,由于您已经在本代码中的IO中,StateT StateData IO / get / modify与ReaderT (IORef StateData) IO / readIORef / modifyIORef没有什么不同。
实际上,如果您将IORef放在StateData中,假设它是代码中一个更复杂的记录类型,那么后者是使它的某些部分不可变和其他部分不可变的好方法。
data StateData = StateData
{ mutableThing :: !(IORef Thing)
, immutableStuff :: !Stuff
…
}https://stackoverflow.com/questions/66859656
复制相似问题