2012-01-24 35 views
1

我做了以下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' 
+0

5个问题,并没有接受答案。请工作。 – leppie

+1

对不起,我不知道它..已经做了修改:) –

回答

5

看起来你很困惑如何使用符号。注释用于编写monad中的操作。在main的情况下,该monad将为IO,因此我将坚持使用IO以保持简单。

有三种类型,您可以在DO-符号使用的语句:

  1. x <- foo结合运行的操作foo的模式x的结果。 foo必须具有类型IO Somethingx将具有相应的类型Something

  2. let x = foo绑定一个没有任何特殊事件的值。这与顶层的=相同,区别在于前面的任何绑定都在范围之内。

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

在你的代码的主要问题是,你正在使用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" 
2

我将从第二个问题开始。

newtype Value a = Value Int deriving (Eq,Read,Show) 

这实际上创建 Value S:

  • newtype Value aValue类型构造
  • Value IntValue数据构造(通常叫它一个构造函数)

他们是不同的东西!

module ExampleProblem (Value,read',load,incr) where 

这里Value表示类型构造函数。要导出数据的构造,以及,你需要

module ExampleProblem (Value(Value),read',load,incr) where 

现在,关于你的第一个问题。

  • main的类型必须是IO something

你已经设置main是一个do块,所以这意味着

  • 一切的<-权类型必须IO somethingOrOther

与错误消息中的行

(Value ab) <- (load 42) 

load 42具有类型Value Int,显然无关IO,所以你得到一个错误。

那么你如何解决这个问题呢?

  • 如果代码在do块行不是一个IO的语句,它必须是一个let语句

其他错误需要修正:

  • return不会做任何其他语言的功能。 特别是,它总是会返回一个值。我们应该叫它别的东西。对于那个很抱歉。 想象一下,它被称为pure。无论如何,你不需要这里。
  • 要在屏幕上打印String,您应该使用putStrLn而不是show。 Ghci打印出你给它的表达式的返回值,但这是一个程序本身,所以你必须自己去做IO。
  • thenelse需要进一步缩进比if(我觉得这个规则被改变,但我不认为它尚未)

所以我们最终

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" 

脚注:

  1. 并不完全正确:do块可以用于IO以外的其他功能。但你稍后会了解这一点。
2

一个附加注释:你应该尝试做的主要-IO的事情尽可能外面很多工作。在你的情况下,很容易:你有一个计算,它不需要参数,并产生一个字符串(应该打印出来)。如果你有不同的“结果”,你可以使用例如Either,但这里我们没有问题,只是String作为返回类型。

正如Hammar指出的那样,模式匹配并没有太多意义,并且一直重构Value,只是使用它们的值,并且只有在需要访问内部数字时才使用模式匹配。

该类型的值不需要是多态的,如果它总是只包含Int,所以我放弃了a。否则你有一种叫做“幻像类型”的东西,这可能有时候甚至是有用的,但绝对不是这里的(当然,如果你想能够包装任意类型的类型,你可以写data Value a = Value a)。

因此,这里是一个版本,只做在IO最低限度,并保留一切纯(因此灵活的,可测试等):

data Value = Value Int deriving (Eq,Read,Show) 

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

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

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

main = putStrLn calc 

calc :: String 
calc = let ab = load 42 
     in if read' ab /= 42 then "Failure to load" else increaseTwice ab 

increaseTwice :: Value -> String 
increaseTwice v = let b = incr v 
         c = incr b 
        in if c == Value 44 then "Example finished" else "error" 

我不能在这里使用的Haskell,所以我希望这项工作...