Haskell,我可以调用没有IO输出的函数与monads一起工作吗?

时间:2012-04-01 03:58:33

标签: haskell io monads

为什么我不能这样做? 禁止在这个问题中使用'do':/ 如何在我的列表中调用单词并同时产生IO? 谢谢..这是我的实际代码:/

main :: IO()
main = 
     putStr "Name of File: " >>
     getLine >>=
     \st ->
    openFile st ReadMode >>=
    \handle ->
        hGetContents handle >>=
        \y ->
        words y >>=
        \strings ->
            strings !! 1 >>=
            \string->
                 putStr string

[编辑]解决方案:

main :: IO()
main = 
     putStr "Name of File: " >>
     getLine >>=
     \st ->
    openFile st ReadMode >>=
    \handle ->
        hGetContents handle >>=
        \y ->
        return (words y) >>=
        \strings ->
            return (strings !! 1) >>=
            \string->
                 putStr string

2 个答案:

答案 0 :(得分:6)

使用return (words y)代替words yreturn将纯值(例如[String]返回的words)包装到monad中。

从你的措辞来看,听起来这个问题就是家庭作业。如果是这样,它应该被标记为。

答案 1 :(得分:6)

(这并没有直接回答这个问题,但它会使你的代码更加惯用,从而更容易阅读。)

你正在使用模式\x -> f x >>= ...,这可以(并且应该)被消除:它(大部分)是不必要的噪声,它掩盖了代码的含义。我不会使用你的代码,因为它是作业,但请考虑这个例子(注意我正在使用return,如其他答案所示):

main = getLine >>= 
         \fname -> openFile fname ReadMode >>= 
           \handle -> hGetContents handle >>= 
              \str -> return (lines str) >>= 
                \lns -> return (length lns) >>= 
                  \num -> print num

(它从用户读取文件名,然后打印该文件中的行数。)

最简单的优化是我们计算行数的部分(这对应于您将单词分开并得到第二行的部分):字符串str中的行数仅为{ {1}}(与length (lines str)相同),因此我们没有理由对length . lines $ str进行调用,并且对length的调用分开。我们的代码现在是:

lines

现在,下一个优化工作在main = getLine >>= \fname -> openFile fname ReadMode >>= \handle -> hGetContents handle >>= \str -> return (length . lines $ str) >>= \num -> print num 。这可以写成\num -> print num。 (这称为eta conversion)。 (您可以将此视为“一个接受参数并在其上调用print的函数,与print本身相同”。现在我们有:

print

我们可以做的下一个优化基于monad laws。使用第一个,我们可以将main = getLine >>= \fname -> openFile fname ReadMode >>= \handle -> hGetContents handle >>= \str -> return (length . lines $ str) >>= print 转换为return (length . lines $ str) >>= print(即“创建一个包含值的容器(这由print (length . lines $ str)完成),然后将该值传递给return }只是将值传递给print“)。同样,我们可以删除括号,所以我们有:

print

看!我们可以执行eta转换:main = getLine >>= \fname -> openFile fname ReadMode >>= \handle -> hGetContents handle >>= \str -> print . length . lines $ str 变为\str -> print . length . lines $ str。这留下了:

print . length . lines

此时,我们可能会停止,因为该表达式比原始表达式简单得多(如果我们愿意的话,我们可以继续使用>=>)。由于它更简单,所以它也更容易调试(想象一下,如果我们忘记使用main = getLine >>= \fname -> openFile fname ReadMode >>= \handle -> hGetContents handle >>= print . length . lines :在原始的lines中它不是很清楚,在最后一个中它是显而易见的。 )

在您的代码中,您可以而且应该这样做:您可以使用sections之类的内容(这意味着main\x -> x !! 1相同),以及eta转换和monad我上面用的法律。