Haskell:I/O 和从函数返回

Posted

技术标签:

【中文标题】Haskell:I/O 和从函数返回【英文标题】:Haskell: I/O and Returning From a Function 【发布时间】:2011-05-02 16:40:30 【问题描述】:

请多多包涵,因为我对函数式编程和 Haskell 还很陌生。我正在尝试在 Haskell 中编写一个函数,该函数采用整数列表,打印所述列表的头部,然后返回列表的尾部。该函数需要是 [Integer] -> [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] 的原因。有什么方法可以忽略打印的返回类型,因为我需要返回的只是传递给点的列表的尾部。

提前致谢。

【问题讨论】:

没有。这是 Haskell,而不是 C - return 不是保留字,do-blocks 不需要以 while-statement 终止。 【参考方案1】:

在大多数函数式语言中,这都是可行的。然而,Haskell 是一种函数式语言。你不能在函数中做IO,所以函数可以是

    [Int] -> [Int] 不执行任何 IO 或 [Int] -> IO [Int] 带 IO

编译器推断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 应该非常小心地使用它并对其后果有深刻的理解。

【讨论】:

Debug.Trace 模块也可以用来打印东西。【参考方案2】:

不,您的函数可能会导致副作用(又名 IO,在本例中是在屏幕上打印),或者不会。 print 执行 IO,因此在 IO 中返回一些内容,这无法撤消。

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

但是类型系统会保护你并确保所有使用IO 的函数,即使只是间接使用,都有一个IO 类型来反映这一点。如果你想要一个纯函数,你不能在其中使用print

【讨论】:

【参考方案3】:

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

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

如果不使用 hack,这意味着您需要将 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

【讨论】:

以上是关于Haskell:I/O 和从函数返回的主要内容,如果未能解决你的问题,请参考以下文章

Haskell -- 并发 I/O 路由

Haskell:未经请求的unicode字符在i / o中转义

函数返回与函数参数中的 Haskell 模式匹配

Haskell检查函数是不是两次返回相同的值

为什么Haskell函数定义里面返回参数和返回类型都用 -> 分隔

Haskell入门篇八:高阶函数(上)