简体   繁体   English

Haskell无法将类型`Stack'与`IO'匹配

[英]Haskell Couldn't match type `Stack' with `IO'

this is my first time to use Haskell and i have read many many tutorials about it. 这是我第一次使用Haskell,我已经阅读了很多关于它的教程。 But when it came to practice, many problems show up. 但是在实践中,出现了许多问题。 I m trying to make a stack data structure and use it in the Do block. 我试图建立一个堆栈数据结构并在Do块中使用它。 But when i do this. 但是当我这样做的时候。 It says cant match type'Stack' with 'IO', i have no idea about this problem. 它说不能匹配类型'堆'与'IO',我不知道这个问题。 Following is my code: 以下是我的代码:

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

Problem is below(when i put push 0 mystack in the Do block it shows up) 问题在下面(当我把推出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

The problem here is that main has type IO () , meaning that any statement inside the do block must have type IO a for some type a . 这里的问题是main具有类型IO () ,这意味着do块中的任何语句必须具有某种类型a类型IO a a Your data type is Stack a , which does not match IO a . 您的数据类型是Stack a ,它与IO a不匹配。 You also look like you're wanting some sort of "mutable state" with your stack, but all your functions are pure, meaning they simply return a new value. 你也看起来像是想要你的堆栈的某种“可变状态”,但你的所有函数都是纯粹的,这意味着它们只返回一个新值。 Values in Haskell are immutable , meaning that they can't be modified after being declared. Haskell中的值是不可变的 ,这意味着它们在声明后不能被修改。 For most purposes, Haskell doesn't have variables, just named values. 在大多数情况下,Haskell没有变量,只有命名值。

What you probably really want is to use the State monad. 你可能真正想要的是使用State monad。 You could modify your push and pop functions to work in that monad instead, and then use execState to run the stateful computation: 您可以修改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'

Notice how easy it was to directly use your already written functions to implement this! 请注意直接使用已编写的函数来实现它是多么容易! You even had pop return the Maybe a in the first element of the tuple to go straight into the state function. 你甚至在元组的第一个元素中pop返回Maybe a直接进入state函数。 You can then use this as 然后你可以用它作为

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

This will print out 这将打印出来

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

push 0 mystack returns a new stack. push 0 mystack返回一个新堆栈。 You are not getting the return value, and you are writing this line as an "action". 您没有获得返回值,并且您将此行写为“操作”。 "Actions" are things that change the global state of the system, they are marked by functions returning IO. “动作”是改变系统全局状态的东西,它们由返回IO的函数标记。 Since push doesn't change the global state, haskell tells you that there's no reason to call it like you do. 由于push不会改变全局状态,因此haskell告诉你没有理由像你那样调用它。

What you probably mean is: 你的意思是:

let newStack = push 0 mystack

Inside a do block, consecutive lines like: do块内,连续的行如:

do
  print list
  print "something else"

get translated to: 被翻译成:

print list >> print "something else"

where >> has type IO a -> IO b -> IO b when using IO as you are here. 其中>>具有类型IO a -> IO b -> IO b ,因为您在这里使用IO

So for: 因此对于:

print list
push 0 mystack

to compile, push 0 mystack must return an IO a for some type a , however push 0 mystack returns a Stack Integer , hence the error. 要编译, push 0 mystack必须为某个类型a返回一个IO a ,但是push 0 mystack返回一个Stack Integer ,因此出错。

If you want to bind a regular value inside a do block you can use let eg 如果要在do块中绑定常规值,可以使用let eg

let stack' = push 0 mystack

Other answers have already proposed a few solutions. 其他答案已经提出了一些解决方案。 Here, let me comment about what your code actually means. 在这里,让我评论一下你的代码实际意味着什么。 Let us focus on your main : 让我们专注于您的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()

As the error points out, the problem lies in the push 0 mystack line. 正如错误所指出的那样,问题在于push 0 mystack行。 Now, Haskell is a pure language in which every defined value (such as push , or mystack ) can be replaced by its own definition without affecting the program meaning. 现在,Haskell是一种纯语言,其中每个定义的值(例如pushmystack )都可以被其自己的定义替换而不会影响程序的含义。 This feature is often called "referential transparency". 此功能通常称为“参照透明度”。 We can then state that the line 然后我们可以说明这一行

push 0 mystack

is equivalent to (by definition of mystack ) 相当于(根据mystack的定义)

push 0 empty

which is equivalent to (by definition of empty ) 相当于(按empty的定义)

push 0 (Stack [])

which is equivalent to (by definition of push ) 相当于(按push的定义)

Stack (0:[])

which can be written using the more common syntax as 可以使用更常见的语法编写

Stack [0]

Hence, your main actually means 因此,你的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()

Now it is easy to see that the problematic line is indeed such. 现在很容易看出问题线确实如此。 It mentions a Stack value, but it does not specify what to do on that. 它提到了一个Stack值,但它没有指定该怎么做。 We could, for instance, just print it using 例如,我们可以使用它来打印它

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

or we could define a variable with such value 或者我们可以定义具有此值的变量

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

In general we could do anything with such a value. 一般来说,我们可以用这样的价值做任何事情。


In the case you were trying to "modify" the value of mystack , know that you can not in a pure language as Haskell. 在你试图“修改” mystack的值的mystack ,要知道你不能用纯语言作为Haskell。 Indeed, to modify the value of mystack is as meaningful as modifying the value of Stack [] , by referential transparency. 实际上,修改mystack的值与通过引用透明度修改Stack []的值一样有意义。 Haskell variables denote values rather than mutable memory cells. Haskell变量表示值而不是可变存储单元。 Perhaps surprisingly at first, one can often program without mutable variables. 也许首先令人惊讶的是,人们通常可以编程而没有可变变量。 This might feel impossible, or impractical, when one has been programming in an imperative language since a long time, but once accustomed with pure functional programming this feels very natural, and pays back in many ways (eg allowing equational reasoning about code, much fewer concerns on concurrent programming, etc.). 这可能感觉不可能或不切实际,因为很久以来一直用命令式语言进行编程,但是一旦习惯了纯函数编程,这种感觉非常自然,并且在很多方面得到回报(例如,允许对代码进行等式推理,更少的对并发编程的关注等)。 When you really need mutable state, you can use the State monad, or the IO moand and IORef s, or ST and STRef , for instance. 当你真的需要可变状态时,你可以使用State monad,或IO moand和IORef ,或者STSTRef These however should be used when the stateful approach is really the best, simplest approach to express your algorithm. 但是,当有状态方法确实是表达算法的最佳,最简单的方法时,应该使用这些方法。 In practice, this is rarely so, and even when it is, often one can still write most of the subroutines without having side effects on the state. 实际上,这种情况很少发生,即使是这样,通常人们仍然可以编写大部分子程序而不会对状态产生副作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM