I want to write a text interface, which provides some default commands. This program supports tab completion of those commands.
This program also records user inputs and stores it in StateData. And now I want this program to support tab completion of those user inputs. For example:
*Main > main
> read a<tab> -- press tab and no suggestions (read is a default command)
> read abcde
...
> read a<tab> -- press tab
abcde -- suggestions
Is it possible to do that without using unsafe mechanism like IORef? Is there a way to pass updated st from loop (in repl) to replSettings startState (in repl)?
I am new to Haskeline and thanks for your time.
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 = ...
IORefisn’t unsafe; you’re already inIO, so it’s a perfectly reasonable way to add mutable state here.But if you want to avoid
IO, you can simply useStateT StateData IOas the underlying monad forInputT, and thus the completion function inSettings. It seems you’re already trying to useStateTanyway. Here’s a complete example that just adds every entry to a list and autocompletes them naïvely:The completion generator could also be written using
MonadState(frommtl) to insulate it from being able to accessIO, and other code could likewise use this pure state while being agnostic toIO. But otherwise, since you’re already inIOin this code,StateT StateData IO/get/modifyare no different thanReaderT (IORef StateData) IO/readIORef/modifyIORef.In fact, if you put an
IORefinStateData, supposing it’s a more complex record type in your code, the latter is a good way to make some parts of it mutable and others immutable.