解析getArgs结果时出现简单类型错误

时间:2017-01-28 19:05:54

标签: haskell

我正在尝试一个非常简单的参数解析器this example。 第一个参数应该是一个double,第二个参数应该是一个整数,如果它们不是这两个类型,我想对else中指定的两个使用默认参数。这就是我所拥有的:

parseArgs :: [String] -> (Double, Integer)
parseArgs args = do
  if length(args) == 2 then do
    let v1 = read (args !! 0) :: Double
    let v2 = read (args !! 1) :: Integer
    return (v1, v2)
  else do
    let v1 = read ("0.5") :: Double 
    let v2 = read ("5") :: Integer
    return (v1, v2)

我知道有更复杂的方法可以使用Applicative使用由optparse启发的设计来解析参数,但我还没有。

这是我得到的错误:

myscript.hs:186:5-31: error: …
    • Couldn't match type ‘(Double, Integer)’ with ‘Integer’
      Expected type: (Double, Integer)
        Actual type: (Double, (Double, Integer))
    • In a stmt of a 'do' block: return (v1, v2)

我不明白这一点。看一下getArgs的签名,我没有看到任何奇怪的东西,表明我无法恢复我的int,或者它应该返回(Double,Integer)而不仅仅是{{1} }}

我该如何正确地做到这一点?

2 个答案:

答案 0 :(得分:6)

您不需要在此处使用do表示法,因为这是为了处理某些monadic类型。您只需匹配输入列表:

parseArgs :: [String] -> (Double, Integer)
parseArgs [d, i] = (read d, read i)
parseArgs _ = (0.5, 5)

答案 1 :(得分:5)

在我看来,你在" 命令式"中有一些经验。 return似乎是从函数返回内容的关键字的世界。

然而,在Haskell中,return语句用于定义/使用monad。顺便提一下do块也一样。您的类型(Int,Double)可以用作monadic类型(为@duplode提供赞誉)。但在这种情况下,它看起来并不像你想要/必须使用monad,因为这看起来像一个简单的函数。

所以你可以用以下方法解决问题:

parseArgs :: [String] -> (Double, Integer)
parseArgs args =
  if length(args) == 2 then
    let v1 = read (args !! 0) :: Double
        v2 = read (args !! 1) :: Integer
    in (v1, v2)
  else
    let v1 = read ("0.5") :: Double 
        v2 = read ("5") :: Integer
    in (v1, v2)

因此,您使用in表示您在表达式中使用v1v2等。它仍然不是很好的#Haskell-ish"。更好的方法是使用警卫(放下if-else):

parseArgs :: [String] -> (Double, Integer)
parseArgs args | length args == 2 =
                     let v1 = read (args !! 0) :: Double
                         v2 = read (args !! 1) :: Integer
                     in (v1, v2)
               | otherwise =
                     let v1 = read ("0.5") :: Double 
                         v2 = read ("5") :: Integer
                     in (v1, v2)

最后,我真的不明白你为什么要使用所有这些let语句并指定类型。您只需将其重写为:

parseArgs :: [String] -> (Double, Integer)
parseArgs args | length args == 2 = (read (args !! 0), read (args !! 1))
               | otherwise = (read "0.5", read "5")

现在我们还没有完成。由于args的长度为2,因此它的形状为[a,b]。我们可以在头脑中使用这种模式:

parseArgs :: [String] -> (Double, Integer)
parseArgs [a,b] = (read a, read b)
parseArgs _     = (read "0.5", read "5")

优势在于您不再需要使用(!!)来获取 i -th元素:检查和匹配可以同时完成。

我建议的最后一项改进是在第二种情况下省略read:您只需输入0.55

parseArgs :: [String] -> (Double, Integer)
parseArgs [a,b] = (read a, read b)
parseArgs _     = (0.5, 5)