我做了以下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)
您似乎对如何使用do-notation感到困惑。 Do-notation用于在 monad 中编写动作。对于main
,该monad将为IO
,因此我会坚持使用IO
来保持简单。
您可以在do-notation中使用三种类型的语句:
x <- foo
将操作foo
的结果绑定到模式x
。 foo
必须包含IO Something
类型,x
将具有相应的类型Something
。
let x = foo
绑定一个值而不会发生任何特殊情况。这与顶级=
相同,只是前面的任何绑定都在范围内。
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"
答案 1 :(得分:2)
还有一句话:你应该尝试在主IO之外做尽可能多的工作。在你的情况下,这很容易:你有一个不带参数的计算并产生一个字符串(应该打印出来)。如果你有不同的“结果”,你可以使用例如Either
,但我们可以将String
作为返回类型。
正如hammar所指出的那样,模式匹配并一直重构Value
没有多大意义,只需按原样使用这些值,并且只有在需要访问内部数字时才使用模式匹配。
如果类型值始终仅包含Int
,则类型值不需要是多态的,因此我删除了a
。另外你有一种叫做“幻影类型”的东西,它有可能,有时甚至是有用的,但绝对不是在这里(当然如果你想能够包装任意类型,你可以写{{1 }})。
所以这是一个只在data Value a = Value a
中只做最小的版本,并保持其他一切纯粹(因而灵活,可测试等):
IO
我不能在这里使用Haskell,所以我希望这有效......