我正在制作控制台程序,我将大量使用文本菜单.我写了一个Menu带有函数的类,choices它返回可能的菜单选项parseChoice字符串和将用户输入的字符串转换为菜单项的函数.
data MainMenu = FirstItem | SecondItem
class Menu a where
choices :: String -- ERROR HERE
parseChoice :: String -> Maybe a
instance Menu MainMenu where
choices = "1) first choice\n2) second choice"
parseChoice "1" = Just FirstItem
parseChoice "2" = Just SecondItem
parseChoice _ = Nothing
getMenuItem :: Menu a => IO a
getMenuItem = do
putStrLn choices -- ERROR HERE
choice <- getLine
case parseChoice choice of
Just item -> return item
Nothing -> getMenuItem
main :: IO ()
main = (getMenuItem :: IO MainMenu) >> return ()
Run Code Online (Sandbox Code Playgroud)
不幸的是,我遇到了以下错误
• Could not deduce (Menu a0) arising from a use of ‘choices’
from the context: Menu a
bound by the type signature for:
getMenuItem :: Menu a => IO a
at [removed].hs:15:1-29
The type variable ‘a0’ is ambiguous
These potential instance exist:
instance Menu MainMenu
-- Defined at [removed].hs:9:10
• In the first argument of ‘putStrLn’, namely ‘choices’
In a stmt of a 'do' block: putStrLn choices
In the expression:
do { putStrLn choices;
choice <- getLine;
case parseChoice choice of {
Just item -> return item
Nothing -> getMenuItem } }
Run Code Online (Sandbox Code Playgroud)
我知道发生错误是因为Haskell不知道choices使用哪个函数.我尝试了类似putStrLn (choices :: Menu a)但没有成功的事情.
问题是:问题在哪里(以及如何解决)?我应该使用不同的方法(哪个)?
请礼貌,我是Haskell新手.
谢谢.
@porges对于为什么会发生这种情况是正确的,编译器根本没有足够的信息来知道类型类的哪个实例choices将来自哪个.相反,您可以尝试使用幻像类型标记它:
data Choices a = Choices String
class Menu a where
choices :: Choices a
parseChoices :: String -> Maybe a
Run Code Online (Sandbox Code Playgroud)
仅这一点还不够,你需要在任何地方使用它来注释choices:
putStrLn (choices :: Choices a)
Run Code Online (Sandbox Code Playgroud)
但这并不是很理想.另一种方法是完全放弃类型类方法并坚持使用基本数据类型:
data Menu a = Menu
{ choices :: String
, parseChoices :: String -> Maybe a
}
Run Code Online (Sandbox Code Playgroud)
那你可以做
data MainMenu = FirstItem | SecondItem
mainMenu :: Menu MainMenu
mainMenu = Menu _choices _parseChoices where
_choices = "1) first choice\n2) second choice"
_parseChoices "1" = Just FirstItem
_parsechoices "2" = Just SecondItem
_parseChoices _ = Nothing
Run Code Online (Sandbox Code Playgroud)
最后
getMenuItem :: Menu a -> IO a
getMenuItem menu@(Menu choices parseChoices) = do
putStrLn choices
choice <- getLine
case parseChoice choice of
Just item -> return item
Nothing -> getMenuItem menu
main :: IO ()
main = (getMenuItem mainMenu) >> return ()
Run Code Online (Sandbox Code Playgroud)