使用'print'不会产生(Show(IO()))的实例

时间:2016-05-09 15:18:32

标签: haskell

只是为了学习我在我的控制台WinGHCi上做了这个:

let ls = [putChar 'x',putChar 'y']

然后如果:

head ls

输出是显而易见的 x (从某种意义上说,我不明白为什么)

否则,如果我这样做:

tail ls

我收到此错误:

  

使用'print'

时没有(Show(IO()))的实例      

在交互式GHCi命令的stmt中:打印它

为什么呢?不应该输出 y [putChar 'y']

4 个答案:

答案 0 :(得分:13)

此行为已解释为here

head ls的类型为IO (),因此GCHi会执行操作,但不会打印结果()

相比之下,tail ls的类型为[IO ()]。由于这不是IO操作,GCHi会尝试使用print来显示它,其类型为:

print :: Show a => a -> IO ()

但是,由于Show没有[IO ()]个实例,您会收到错误消息。如果您想评估可以使用sequence_

的操作
sequence_ (tail ls)

答案 1 :(得分:6)

  

然后如果:head ls    输出是显而易见的x

好吧,actually not obviously!事实上,head ls并不是与角色'x'直接相关的东西。首先,它只是一个IO动作,GHCi假设这个动作做了你想要发生的事情:它有一个特殊的处理方式,对于IO动作,它试图不显示该动作,但执行它。嗯,很好,你没有考虑清单[launchMissiles, putStrLn "oops"] ......

现在,执行操作确实会导致将字符'x'打印到控制台,这恰好也是您的视口,因此您可以将其视为仅仅是putChar 'x'的值。但事实并非如此!

OTOH,tail ls来自外部只是一个列表,所以ghci并不假设您想要执行任何操作。它确实尝试显示列表,但您无法显示 IO操作或其中包含IO操作的任何操作;这是错误信息所说的内容。

答案 2 :(得分:5)

当你有

> let ls = [putChar 'x', putChar 'y']

您没有执行存储在该列表中的操作,您只是构建这些操作,如果这有意义的话。你可以有像

这样的东西
printHelp :: IO ()
printHelp = putStrLn "You need to pass XYZ options to this program"

在文件中定义。这不会立即打印出消息,而是构造一个动作,一旦执行,就会将该消息打印到控制台。执行发生在两个地方之一:main函数和GHCi。

看起来你正在使用GHCi来执行代码,这对于探索不同的函数来说非常棒,但与在文件中执行代码有点不同。每当您在GHCi中自行评估IO Something操作时,它就会运行该IO操作。每当您评估GHCi中的任何其他值时,它会在该行前面插入print。所以当你这样做时

> head ls

此类型为IO (),因此执行操作putChar 'x',导致x打印到控制台。当你这样做

> tail ls

这个类型为[IO ()],因此GHCi会尝试在其前面添加print,使其等同于

> print (tail ls)

print函数的类型为

print :: (Show a) => a -> IO ()

我们已在此处向print传递了一个列表,并且列表中有Show的实例,但仅当列表的元素具有Show的实例时。因此编译器会尝试查看IO ()是否有Show的实例。不幸的是,它没有,因为IO是一个非常复杂的类型,表示几乎任何事情都可能发生。因此,编译器显示IO ()没有Show实例的错误。

如果您希望按顺序运行列表中的每个操作,则有一个函数:Control.Monad.sequence(如果您不关心结果,则为Control.Monad.sequence_)。此功能实际上适用于所有Monad,而不仅仅是IO

> sequence_ (tail ls)
y> sequence_ ls
xy>

答案 3 :(得分:0)

不仅tail ls,如果您执行lsinit ls,您将收到相同的错误。

ls的类型是:

ls :: [IO ()]

当您在GHCi中键入内容时,它将评估表达式并打印结果。

在第二种情况(ls)中,ls类型的返回值是一系列操作,GHC不知道如何操作显示IO ()的操作。

在第一种情况下,GHCi评估打印x的IO ()IO ()无返回值,因此无需显示任何内容且无错误。