在haskell wikibook中,有人要求写下这样的东西:
adding = do
putStrLn "enter first number"
first <- readMaybe <$> getLine
putStrLn "enter second number"
second <- readMaybe <$> getLine
let x = (+) <$> first <*> second :: Maybe Double
case x of
Just d -> putStrLn $ "the sum is " ++ show d
Nothing -> do
putStrLn "not good"
adding
这要求两个数字,然后添加它们。但是我试着把它写得更短一些,在同一行上做两个getlines:
adding2 = do
putStrLn "enter two numbers"
x <- (+) <$> (readMaybe <$> getLine) <*> (readMaybe <$> getLine)
case x of
Just d -> putStrLn $ "the sum is " ++ show d
Nothing -> do
putStrLn "not good"
adding
但这不起作用。它抱怨d的类型,我怀疑是因为我还没有说任何地方它应该是一个可能的双倍。
或者可能是(+) <$> (readMaybe <$> getLine) <*> (readMaybe <$> getLine)
行?
我该如何解决这个问题?
显然问题是我有两个不同的单子,不能做我希望做的事情。我写了一个我特别满意的变体:
adding5 :: IO ()
adding5 = do
putStrLn "enter two numbers"
let a = readMaybe <$> getLine
a >>= \ a1 -> a >>= \ a2 ->
case (+) <$> a1 <*> a2 of
Just d -> putStrLn $ "the sum is " ++ show d
Nothing -> do putStrLn "not good"
adding5
以下是评论中amalloy建议之后的另一个建议:
adding6 = do
putStrLn "enter two numbers"
[a1, a2] <- replicateM 2 (readMaybe <$> getLine)
case (+) <$> a1 <*> a2 of
Just d -> putStrLn $ "the sum is " ++ show d
Nothing -> do putStrLn "not good"
adding6
答案 0 :(得分:2)
在原始功能中,你有
first :: Maybe Double
second :: Maybe Double
因此,当您将它们与<$>
和<*>
结合使用时,您正在使用Maybe语境运行:正是您想要的。
但是在你的新函数中,<*>
的第二个参数是
readMaybe <$> getLine :: Read t => IO (Maybe t)
您现在在IO上下文中使用<$>
和<*>
,而不是Maybe上下文,因此您尝试添加的内容属于Maybe t
类型,而不是{{ 1}}。无论t
是什么,都无法添加Maybe t
类型的值,因为t
不是Maybe
。因此,即使您向编译器提供足够的信息来得出Num
应该是t
的结论,这也行不通。
您必须将IO值绑定到本地解包名称,就像在第一个函数中一样,或者等效地使用Double
在您拥有的>>=
内部进行操作。