Haskell无法匹配类型`Stack'用'IO'

时间:2014-07-30 19:11:22

标签: arrays haskell io

这是我第一次使用Haskell,我已经阅读了很多关于它的教程。但是在实践中,出现了许多问题。 我试图建立一个堆栈数据结构并在Do块中使用它。但是当我这样做的时候。它说不能匹配类型' Stack'和' IO',我不知道这个问题。以下是我的代码:

import Data.Array.IO

main::IO()
main = do
     arr <- newArray((0,0),(8,13)) 0 ::IO (IOArray(Int,Int) Int) 
     list <- getElems arr
     print list
     push 0 mystack   --here is the problem
     return()

data Stack a = Stack [a] deriving Show

empty :: Stack a
empty = Stack []

push :: a -> Stack a -> Stack a 
push x (Stack xs)= Stack (x:xs)

pop :: Stack a -> (Maybe a, Stack a)
pop (Stack []) = (Nothing, Stack [])
pop (Stack (x:xs)) = (Just x, Stack xs)

mystack = empty

问题如下(当我将推送0 mystack放入它显示的Do块时)

Couldn't match type `Stack' with `IO'
    Expected type: IO Integer
      Actual type: Stack Integer
    In the return type of a call of `push'
    In a stmt of a 'do' block: push 0 mystack

4 个答案:

答案 0 :(得分:5)

此处的问题是main的类型为IO (),这意味着do块中的任何语句都必须为IO a类型的a类型。您的数据类型为Stack a,与IO a不匹配。你也看起来像是想要某种可变状态&#34;使用您的堆栈,但所有功能都是纯粹的,这意味着它们只返回一个新值。 Haskell中的值是 immutable ,这意味着它们在声明后不能被修改。在大多数情况下,Haskell没有变量,只是命名值。

你真正想要的是使用State monad。您可以修改pushpop函数以在该monad中工作,然后使用execState运行有状态计算:

import Control.Monad.State

data Stack a = Stack [a] deriving (Eq, Show)

push' :: a -> Stack a -> Stack a
push' x (Stack xs) = Stack (x:xs)

push :: a -> State (Stack a) ()
push x = modify (push' x)

pop' :: Stack a -> (Maybe a, Stack a)
pop' (Stack []) = (Nothing, Stack [])
pop' (Stack (x:xs)) = (Just x, Stack xs)

pop :: State (Stack a) (Maybe a)
pop = state pop'

请注意直接使用已编写的函数来实现它是多么容易!您甚至让pop返回元组的第一个元素中的Maybe a以直接进入state函数。然后,您可以将其用作

main :: IO ()
main = do
    let resultStack = flip execState empty $ do
            push 1
            push 2
            push 3
            pop                    -- pop off 3
            Just x <- pop          -- pop off 2
            push $ 2 * x           -- push 4
            mapM_ push [1..10]     -- Pushes 1 through 10 onto the stack in that order
            pop                    -- pop off 10
            pop                    -- pop off 9
            pop                    -- pop off 8
    print resultStack

这将打印出来

Stack [7, 6, 5, 4, 3, 2, 1, 4, 1]

答案 1 :(得分:2)

push 0 mystack返回一个新堆栈。您没有获得返回值,并且您将此行写为“操作”。 “动作”是改变系统全局状态的东西,它们由返回IO的函数标记。由于push不会改变全局状态,因此haskell告诉你没有理由像你一样调用它。

你的意思是:

let newStack = push 0 mystack

答案 2 :(得分:2)

do块内,连续的行如:

do
  print list
  print "something else"

转换为:

print list >> print "something else"

>>使用IO a -> IO b -> IO b时,IO的类型为print list push 0 mystack

所以:

push 0 mystack

要编译,IO a必须为某种类型a返回push 0 mystack,但是Stack Integer会返回do,因此会出错。

如果要在let块中绑定常规值,可以使用let stack' = push 0 mystack ,例如

{{1}}

答案 3 :(得分:0)

其他答案已经提出了一些解决方案。在这里,让我评论一下你的代码实际意味着什么。让我们关注您的main

main = do
     arr <- newArray((0,0),(8,13)) 0 ::IO (IOArray(Int,Int) Int) 
     list <- getElems arr
     print list
     push 0 mystack   --here is the problem
     return()

正如错误所指出的那样,问题在于push 0 mystack行。现在,Haskell是一种纯语言,其中每个定义的值(例如pushmystack)都可以用自己的定义替换,而不会影响程序的含义。此功能通常称为&#34;参考透明度&#34;。然后我们可以说明该行

push 0 mystack

相当于(根据mystack的定义)

push 0 empty

相当于(根据empty的定义)

push 0 (Stack [])

相当于(根据push的定义)

Stack (0:[])

可以使用更常见的语法

编写
Stack [0]

因此,您的main实际上意味着

main = do
     arr <- newArray((0,0),(8,13)) 0 ::IO (IOArray(Int,Int) Int) 
     list <- getElems arr
     print list
     Stack [0]   --here is the problem
     return()

现在很容易看出问题线确实如此。它提到了Stack值,但没有指定要对其执行的操作。例如,我们可以使用

打印它
print (Stack [0])  -- or, equivalently, print (push 0 mystack)

或者我们可以定义一个具有此值的变量

let s = Stack [0]   -- or, equivalently, let s = push 0 mystack
...
print s

一般来说,我们可以用这样的价值做任何事情。


如果你试图&#34;修改&#34; mystack的值,知道你不能用纯语言作为Haskell。实际上,修改mystack的值与通过引用透明度修改Stack []的值一样有意义。 Haskell变量表示值而不是可变存储单元。也许首先令人惊讶的是,人们通常可以编程而没有可变变量。这可能感觉不可能或不切实际,因为很久以来一直用命令式语言进行编程,但是一旦习惯了纯函数编程,这种感觉非常自然,并且在很多方面得到回报(例如,允许对代码进行等式推理,更少的对并发编程的关注等)。当您确实需要可变状态时,您可以使用State monad,或IO moand和IORef,或STSTRef。但是,当有状态方法确实是表达算法的最佳,最简单的方法时,应该使用这些方法。实际上,这种情况很少发生,即使是这样,通常人们仍可以编写大部分子程序,而不会对状态产生副作用。