Haskell写String,简单的问题

时间:2011-01-18 13:53:54

标签: string haskell types io

writeStr []=putChar ' '         
writeStr (x:xs) = (putChar x)(writeStr xs)

您好,并提前感谢,我收到类型错误,它应该是一个简单的答案,但我只是不知道错误来自哪里。

2 个答案:

答案 0 :(得分:4)

你的代码有点奇怪。如果我做对了,你会尝试打印一个字符串。你的方法是放第一个字符串,而不是第二个字符串。但是在Haskell中不可能将这样的两个IO动作组合在一起。再看一下你的教程,这里有两个看起来像:

writeStr []     = return () -- you had putChar ' ',
writeStr (x:xs) = do putChar x -- but this would print a superfluous whtiespace
                     writeStr xs

如果您想按顺序执行多项操作,请使用do-keyword或monadic组合器。它非常简单,就像这样:

do action1
   action2
   action3
   ...

答案 1 :(得分:3)

FUZxxl回答了当前的问题,但是我想通过更多方式编写“writeStr”来扩展它,以更多地了解monad。

正如德尔南在评论中所说,你也可以写

writeStr [] = return ()
writeStr (x:xs) = putChar x >> writeStr xs

这实际上是“do”符号的“desugared”版本。 “>>”运算符用于将monadic动作串联起来。它实际上是“绑定”运算符的专用版本,写成“>> =”。有关详细信息,请参阅this question

但是当你看到这一点时,似乎我们所做的只是将“putChar”应用于参数列表中的每个元素。 Prelude中已经有一个名为“map”的函数用于执行此操作,所以也许我们可以编写:

writeStr xs = map putChar xs

但是当你尝试它时,它将无法工作。如果你进入GHCi并输入以下内容,原因就显而易见了:

:type map putChar "Hello"
[IO ()]

您需要单个“IO()”操作,但这会为您提供一个列表。您需要的是一个将此IO操作列表转换为单个IO操作的函数。幸运的是存在一个。 Prelude包含这两个函数

sequence :: [IO a] -> IO [a]
sequence_ :: [IO a] -> IO ()

第一个用于何时需要结果列表,第二个用于不需要的情况,例如这个。 (在这个答案中,为了清楚起见,我将提供IO特定类型的签名,但重要的是要记住所有这些函数实际上适用于任何 monad。)

现在你可以写:

writeStr xs = sequence_ $ map putChar xs

但有一种方法可以缩短这一点。回想一下“。”运算符,它将两个函数组合在一起,以及Haskell的“currying”函数参数的方式?我们可以将上面的函数重写为:

writeStr = sequence_ . map putChar

这种“无点”风格起初看起来很奇怪;它使“writeStr”看起来更像是一个常量而不是一个函数。但它避免了在阅读代码时跟踪代码周围的变量名称的需要,因此通常是首选。当你把一些复杂的东西作为“映射”或类似的高阶函数的参数时,它也更短更易读。

但我们可以更短。 “sequence.map f”模式非常常见,因此“Control.Monad”模块定义了更多功能来体现它:

mapM :: (a -> IO b) -> [a] -> IO [b]
mapM f = sequence . map f

mapM_ :: (a -> IO b) -> [a] -> IO ()
mapM_ f = sequence_ . map f

所以你最后可以写

writeStr = mapM_ putChar