如何用动态值扩展静态自动补全命令列表?(Haskell程序场景)
实现基于状态的动态命令补全(Haskeline + StateT)
要在你的Haskell程序中实现动态值扩展静态自动补全列表,核心是让Haskeline的补全逻辑能够访问并响应MyDataState的当前状态。这里我们需要把Haskeline的InputT monad和StateT状态monad结合,让补全函数能实时读取当前的mydata和showEven值。
步骤1:构建正确的Monad栈
Haskeline的输入交互运行在InputT IO中,而我们的状态管理需要StateT MyDataState。我们会把StateT嵌套在InputT内部,形成StateT MyDataState (InputT IO)的monad栈——这样既可以处理输入交互,又能随时读取/修改状态。
步骤2:编写动态补全函数
我们需要定义一个CompletionFunc,它运行在上述monad栈中,这样就能在补全时获取当前状态,生成动态的补全项:
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-} import Text.Regex.PCRE import System.Console.Haskeline import System.IO import Control.Monad.State.Strict import qualified Data.ByteString.Char8 as B import Data.Maybe import Data.List import qualified Data.Map as M data MyDataState = MyDataState { mydata :: [Int], showEven :: Bool } deriving (Show) -- 动态补全函数:结合静态命令+动态状态值 dynamicCompletions :: CompletionFunc (StateT MyDataState (InputT IO)) dynamicCompletions = completeWord Nothing " \t" $ \input -> do -- 获取当前的MyDataState状态 currentState <- get let -- 静态命令列表(固定不变的命令) staticCommands = ["add", "toggleEven", "show", "exit"] -- 根据showEven过滤mydata中的值,生成动态补全项 dynamicValues = map show $ if showEven currentState then filter even (mydata currentState) else mydata currentState -- 合并静态命令和动态值,过滤出与输入前缀匹配的项 allCandidates = staticCommands ++ dynamicValues matches = filter (isPrefixOf input) allCandidates -- 将匹配项转换为Haskeline的Completion类型 return $ map simpleCompletion matches
步骤3:配置Haskeline设置并运行主循环
接下来,我们需要把动态补全函数绑定到Haskeline的设置中,然后在状态monad中运行输入循环:
-- 处理输入命令,修改状态 processCommand :: String -> StateT MyDataState (InputT IO) () processCommand line = case words line of ["add", numStr] -> case readMaybe numStr of Just num -> modify (\s -> s { mydata = num : mydata s }) Nothing -> outputStrLn "Invalid number!" ["toggleEven"] -> modify (\s -> s { showEven = not (showEven s) }) ["show"] -> do s <- get outputStrLn $ "Current state: " ++ show s ["exit"] -> return () _ -> outputStrLn "Unknown command! Try add <num>, toggleEven, show, exit" -- 主循环 mainLoop :: StateT MyDataState (InputT IO) () mainLoop = do input <- getInputLineWithInitial "> " ("", "") case input of Nothing -> outputStrLn "Exiting..." Just line -> do processCommand line if line == "exit" then return () else mainLoop main :: IO () main = do -- 初始状态 let initialState = MyDataState { mydata = [1,2,3,4,5], showEven = False } -- 配置Haskeline设置,绑定动态补全 let settings = defaultSettings { complete = dynamicCompletions } -- 运行InputT,同时在StateT中管理状态 runInputT settings $ evalStateT mainLoop initialState
关键细节解释
- 补全逻辑的动态性:每次触发补全(比如按Tab键),
dynamicCompletions都会调用get获取最新的MyDataState,所以补全列表会实时反映mydata的变化和showEven的开关状态。比如当你执行toggleEven后,再按Tab补全数字时,只会显示偶数。 - 静态+动态结合:我们把固定的命令(如
add、toggleEven)和动态生成的mydata值合并,既保留了静态命令的补全,又能根据状态扩展补全项。 - Monad栈的协作:
evalStateT负责初始化状态并运行状态monad,而runInputT负责处理终端输入交互,两者结合实现了带状态的动态交互。
内容的提问来源于stack exchange,提问作者altern
相关产品推荐
相关产品推荐

