为什么我不能在此处放置打印功能语句?

时间:2019-05-20 12:22:34

标签: haskell boolean monads io-monad do-notation

我正在尝试使用try-catch块执行以下代码:

import System.Environment  
import System.IO  
import System.IO.Error  
import Control.Exception

isBinary :: String -> Bool
isBinary ss = do 
    print "In isBinary fn"   -- works if this line is removed.
    let ans = any (\c -> ord c > 127) ss
    ans

toTry :: String -> IO ()  
toTry firline = do
        print "In toTry fn."
        let answer = isBinary firline
        if not answer then do
            print "Sent line not binary: "
        else
            print "Sent line binary"

handler :: IOError -> IO ()  
handler e = putStrLn "Whoops, had some trouble!"  

ss = "this is a test"
main = do 
    toTry ss `catch` handler

但是,我遇到以下错误:

$ runghc trycatch3.hs 

trycatch3.hs:9:9: error:
    • Couldn't match expected type ‘Bool’ with actual type ‘IO Bool’
    • In a stmt of a 'do' block: print "in isBinary fn"
      In the expression:
        do { print "in isBinary fn";
             let ans = any (\ c -> ...) ss;
             return ans }
      In an equation for ‘isBinary’:
          isBinary ss
            = do { print "in isBinary fn";
                   let ans = ...;
                   return ans }

trycatch3.hs:10:30: error:
    • Variable not in scope: ord :: Char -> Integer
    • Perhaps you meant one of these:
        ‘or’ (imported from Prelude), ‘odd’ (imported from Prelude)

如果从isBinary函数中删除了打印语句,该错误就会消失并且程序可以正常运行。

为什么不能在此函数中添加打印语句?

3 个答案:

答案 0 :(得分:4)

答案是“因为类型 ”。具体来说:

isBinary :: String -> Bool
isBinary ss = do 
  ....

由于它是do块,因此isBinary和某些{{的返回类型必须与Monad m => m t的单子类型匹配。 1}}。在这里,由于mtprint "" :: IO (),所以应该是

m

现在

IO

isBinary :: String -> IO Bool isBinary ss = do 再次由于类型而无效。其类型为 print "In isBinary fn" -- works let ans = any (\c -> ord c > 127) ss -- also works ans -- doesn't work ,但必须为ans -首先,由于Bool,此IO Bool块属于do monad;其次,由于整个函数的返回类型。

相反,使用

IO

现在可以使用了,因为print向单子上下文注入了一个值,并且作为最后一个 return ans 块值,它成为了整个return块产生的值(如果do出现在中间,它将do传递到组合计算的下一步。

函数return val必须进行扩展才能使用新的定义:

val

toTry位于toTry :: String -> IO () toTry firline = do print "In toTry fn." -- let answer = isBinary firline -- incorrect, now! answer <- isBinary firline -- isBinary ... :: IO Bool if not answer then do -- answer :: Bool print "Sent line not binary: " else print "Sent line binary" 的右侧,m a位于左侧。

有关<-符号的一般说明,请参见this

答案 1 :(得分:3)

您可能会为toTry中的同一打印行而不是isBinary中的同一打印行感到困惑。区别在于声明:

isBinary :: String -> Bool

这意味着isBinary是一个纯函数(即没有副作用),它接受一个字符串并返回一个布尔值。实际上,您可以将其简化为

isBinary ss = any (\c -> ord c > 127) ss 

甚至使用无点样式

isBinary = any (\c -> ord c > 127)

但是,toTry

toTry :: String -> IO ()

即它需要一个字符串并返回不纯的IO monad(可能会有副作用,例如将文本打印到控制台)。

Haskell是一种语言,它鼓励程序员使用纯函数,并通过强制程序员显式标记不纯代码来使用类型系统对其进行强制。

进一步阅读: What does "pure" mean in "pure functional language"?

答案 2 :(得分:2)

看看您的代码,看来您在print中使用isBinary并不是您要函数执行的组成部分,而只是一个调试打印语句,该语句稍后将被删除。 。在这种情况下,您不想将isBinary的类型更改为String -> IO Bool(有关更多信息,请参见Will Ness' answer),因为您实际上不需要IO除调试外。相反,核心库提供了the Debug.Trace module,可以满足这种情况。有了它,我们可以像这样添加您的调试打印语句:

isBinary :: String -> Bool
isBinary ss = trace "In isBinary fn" $ any (\c -> ord c > 127) ss

然后,一旦您完成调试,就可以删除对trace的使用-值得重复一遍,您以后确实应该这样做。引用Debug.Trace文档:

  

用于跟踪和监视执行的功能。

     

这些对于调查错误或性能问题很有用。它们不应该在生产代码中使用。