Haskell:I / O和从函数返回

时间:2010-10-30 01:29:49

标签: haskell types io monads

请耐心等待我,因为我对函数式编程和Haskell都很陌生。我试图在Haskell中编写一个函数,它接受一个I​​ntegers列表,打印所述列表的头部,然后返回列表的尾部。该函数需要是[Integer]类型 - > [整数]。为了给出一些上下文,我正在编写一个解释器,当在关联列表中查找其各自的命令时调用此函数(键是命令,值是函数)。

这是我写的代码:

dot (x:xs) = do print x
      return xs

编译器提供以下错误消息:

forth.hs:12:1:
Couldn't match expected type `[a]' against inferred type `IO [a]'
  Expected type: ([Char], [a] -> [a])
  Inferred type: ([Char], [a] -> IO [a])
In the expression: (".", dot)

我怀疑点函数中的打印调用是导致推断类型为IO [a]的原因。有什么方法可以忽略打印的返回类型,因为我需要返回的是列表的尾部被传递到点。

提前致谢。

3 个答案:

答案 0 :(得分:8)

在大多数功能语言中,这都可行。但是,Haskell是一种函数式语言。您不能在函数中执行IO,因此该函数可以是

  1. [Int] -> [Int]未执行任何IO或
  2. [Int] -> IO [Int]与IO
  3. 编译器推断的dot类型为dot :: (Show t) => [t] -> IO [t],但您可以将其声明为[Int] -> IO [Int]

    dot :: [Int] -> IO [Int]

    请参阅IO monad:http://book.realworldhaskell.org/read/io.html


    我没有提到System.IO.Unsafe.unsafePerformIO应该谨慎使用并且对其后果有充分的了解。

答案 1 :(得分:8)

不,你的功能会导致副作用(也就是IO,在这种情况下是在屏幕上打印),或者它没有。 <{1}}会执行IO,因此会在print中返回一些内容,但这不能撤消。

如果编译器被欺骗忘记IO,那将是一件坏事。例如,如果在程序中使用相同的参数(例如IO)多次调用[Integer] -> [Integer]函数,则编译器可能只执行一次函数并使用其中的结果函数被“调用”的所有地方。即使您在多个地方调用了该功能,您的“隐藏”打印也只会执行一次。

但类型系统可以保护您,并确保使用[]的所有函数(即使只是间接地)具有IO类型来反映这一点。如果您想要纯函数,则不能在其中使用IO

答案 2 :(得分:6)

您可能已经知道,Haskell是一种“纯粹的”函数式编程语言。出于这个原因,副作用(例如在屏幕上打印值)不是偶然的,因为它们是更主流的语言。这个事实为Haskell提供了许多不错的属性,但是当你所做的只是试图在屏幕上打印一个值时,你会因为不关心它而被原谅。

由于该语言没有引起副作用的直接工具,因此策略是函数可能会产生一个或多个“IO动作”值。 IO操作封装了一些副作用(打印到控制台,写入文件等)以及可能产生的值。您的dot函数正在产生这样的操作。你现在遇到的问题是你需要能够引起IO副作用的东西,以及展开值并可能将它传回你的程序。

如果不诉诸黑客,这意味着您需要将IO操作备份回main功能。实际上,这意味着maindot之间的所有内容都必须位于“IO Monad”中。 “IO Monad”中发生的事情可以说是“IO Monad”。

修改

这是关于在有效的Haskell程序中使用dot函数可以想象的最简单的示例:

module Main where

main :: IO ()
main =
    do
        let xs = [2,3,4]
        xr <- dot xs
        xrr <- dot xr
        return ()

dot (x:xs) =
    do
        print x
        return xs