在haskell中组合两个readMaybe和getLine

时间:2017-11-02 17:41:59

标签: haskell

在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

1 个答案:

答案 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在您拥有的>>=内部进行操作。