You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用动态值扩展静态自动补全命令列表?(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

关键细节解释

  1. 补全逻辑的动态性:每次触发补全(比如按Tab键),dynamicCompletions都会调用get获取最新的MyDataState,所以补全列表会实时反映mydata的变化和showEven的开关状态。比如当你执行toggleEven后,再按Tab补全数字时,只会显示偶数。
  2. 静态+动态结合:我们把固定的命令(如add、toggleEven)和动态生成的mydata值合并,既保留了静态命令的补全,又能根据状态扩展补全项。
  3. Monad栈的协作:evalStateT负责初始化状态并运行状态monad,而runInputT负责处理终端输入交互,两者结合实现了带状态的动态交互。

内容的提问来源于stack exchange,提问作者altern

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:42:50