2

我正在尝试使用 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 函数中删除 print 语句,错误就会消失并且程序运行良好。

为什么我不能在这个函数中加入 print 语句?

4

3 回答 3

4

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

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

由于它是一个do块,返回类型isBinary 必须Monad m => m tsomem和 some的一元类型匹配t。这里,因为print "" :: IO ()mIO,所以它应该是

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块属于IOmonad,因为print; 其次,由于整个函数的返回类型。

相反,使用

    return ans

现在它可以工作了,因为return将一个值注入到 monadic 上下文中,并且作为最后一个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的右侧<-a左侧。

有关符号的一般描述,请参见this 。do

于 2019-05-20T13:00:22.023 回答
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 是一种鼓励使用纯函数的语言,并通过强制程序员显式标记不纯代码来使用类型系统强制执行它。

延伸阅读: “纯函数式语言”中的“纯”是什么意思?

于 2019-05-20T12:55:34.673 回答
2

查看您的代码,似乎您对printin的使用isBinary并不是您希望该函数执行的操作的一个组成部分,而只是一个稍后将被删除的调试打印语句。在这种情况下,您不想更改 to 的类型isBinaryString -> IO Bool有关更多信息,请参阅Will Ness 的回答IO),因为除了调试之外您实际上不需要。相反,核心库提供Debug.Trace适合这种情况的模块。有了它,我们可以像这样添加您的调试打印语句:

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

然后,一旦你完成了调试,你就可以删除trace-- 并且值得重复你真的应该在以后这样做。引用Debug.Trace文档:

跟踪和监控执行的功能。

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

于 2019-05-20T21:36:35.350 回答