haskell程序出错

时间:2012-01-24 08:23:09

标签: haskell

我做了以下haskell程序,它将执行一些加载,读取和增量的基本操作。我收到了类型错误。有人可以告诉我为什么会出现类型错误,我该如何解决呢。

module ExampleProblem (Value,read',load,incr) where
newtype Value a = Value Int deriving (Eq,Read,Show)

read':: Value Int -> Int
read' (Value a) = a

load:: Int -> Value Int
load a = Value a

incr:: Value Int -> Value Int
incr (Value a) = Value (a+1)


main =  do
        (Value ab) <- (load 42)
        if (read'( Value ab) /= 42)
        then show "Failure to load"
        else do
             Value b <- incr( Value ab)
             Value c <- incr( Value b)
             if ((Value c) == Value 44)
             then show "Example finished"
             else show "error"
             return

我得到的错误是:

Couldn't match expected type `Int' with actual type `Value t0'
In the pattern: Value ab
In a stmt of a 'do' expression: (Value ab) <- (load 42)
In the expression:
  do { (Value ab) <- (load 42);
       if (read' (Value ab) /= 42) then
           show "Failure to load"
       else
           do { Value b <- incr (Value ab);
                .... } }

当我创建一个单独的文件,其中我编写了主要功能,我得到了范围的错误,虽然我导入模块ExampleProblem。

Not in scope: data constructor `Value'

2 个答案:

答案 0 :(得分:5)

您似乎对如何使用do-notation感到困惑。 Do-notation用于在 monad 中编写动作。对于main,该monad将为IO,因此我会坚持使用IO来保持简单。

您可以在do-notation中使用三种类型的语句:

  1. x <- foo将操作foo的结果绑定到模式xfoo必须包含IO Something类型,x将具有相应的类型Something

  2. let x = foo绑定一个值而不会发生任何特殊情况。这与顶级=相同,只是前面的任何绑定都在范围内。

  3. foo运行IO Something类型的操作。如果它是do-block中的最后一个语句,则这将成为块的结果,否则结果将被忽略。

  4. 您的代码中的主要问题是您使用x <- foo语句而不是IO个操作。那不行。相反,请使用let x = foo表单。

    其次,show也不是IO行动。它只是一个将内容转换为String的函数。您可能打算使用putStrLn将字符串打印到标准输出。

    第三,return不是像C或Java那样的语句。它是一个函数,给定一个值,产生一个什么都不做并返回值的动作。当你希望它返回纯值时,它通常被用作do-block中的最后一个东西。这是不必要的。

    最后,如果要运行此代码,则必须调用模块Main并且必须导出main函数。最简单的方法是删除module ... where行,名称默认为Main。您通常只希望这一行包含项目中不包含main的模块。

    main =  do
        let Value ab = load 42
        if read' (Value ab) /= 42
          then putStrLn "Failure to load"
          else do
             let Value b = incr (Value ab)
             let Value c = incr (Value b)
             if Value c == Value 44
               then putStrLn "Example finished"
               else putStrLn "error"
    

    这应该可行,但您不必要在Value类型中包装和展开您的值。也许你打算这样的事情:

    main =  do
        let ab = load 42
        if read' ab /= 42
          then putStrLn "Failure to load"
          else do
             let b = incr ab
             let c = incr b
             if c == Value 44
               then putStrLn "Example finished"
               else putStrLn "error"
    

答案 1 :(得分:2)

还有一句话:你应该尝试在主IO之外做尽可能多的工作。在你的情况下,这很容易:你有一个不带参数的计算并产生一个字符串(应该打印出来)。如果你有不同的“结果”,你可以使用例如Either,但我们可以将String作为返回类型。

正如hammar所指出的那样,模式匹配并一直重构Value没有多大意义,只需按原样使用这些值,并且只有在需要访问内部数字时才使用模式匹配。

如果类型值始终仅包含Int,则类型值不需要是多态的,因此我删除了a。另外你有一种叫做“幻影类型”的东西,它有可能,有时甚至是有用的,但绝对不是在这里(当然如果你想能够包装任意类型,你可以写{{1 }})。

所以这是一个只在data Value a = Value a中只做最小的版本,并保持其他一切纯粹(因而灵活,可测试等):

IO

我不能在这里使用Haskell,所以我希望这有效......