这是一个使用haskeline和StateT
转换器创建有状态输入命令循环的简单示例:
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.State.Strict
import Control.Monad.Trans (lift)
import System.Console.Haskeline
main = runStateT (runInputT defaultSettings loop) ""
check ma b fb = maybe b fb ma
commands :: (MonadState String m, MonadIO m) => [(String, [String] -> InputT m ())]
commands = [ ("set", performSet), ("get", performGet) ]
performSet args = lift $ put (head args)
performGet _ = do v <- lift get; outputStrLn $ "v = " ++ show v
loop :: (MonadException m, MonadState String m) => InputT m ()
loop = do
minput <- getInputLine "% "
check minput (return ()) $ \inp -> do
let args = words inp
case args of
[] -> loop
(arg0:argv) -> do
case lookup arg0 commands of
Nothing -> do outputStrLn "huh?"; loop
Just handler -> do handler argv; loop
列表commands
包含所有已识别的命令 - (名称,处理程序)对。我想使用如下表达式获取名称列表:
commandNames = map fst commands
但类型检查器抱怨&#34;由于使用'命令'而导致的(MonadState String m0)没有实例 - 类型变量'm0'是不明确的......&#34;
我需要做些什么来满足类型检查器?
答案 0 :(得分:3)
commands
是多态的,它有一个类型变量m
,但commandNames :: [String]
没有任何类型变量。这意味着(除了一些内置默认值)类型推断无法推断commands
的类型变量。你可以做两件事。您可以为commands
自己提供
commandNames :: [String]
commandNames = map fst (commands :: [(String, [String] -> InputT (StateT String IO) ())])
或者您可以更改代码,以便使用更少的类型定义更多类型变量的值。
commandNames :: [String]
commandNames = ["set", "get"]
commands :: (MonadState String m, MonadIO m) => [(String, [String] -> InputT m ())]
commands = zip commandNames [performSet, performGet]