我正在尝试使用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函数中删除了打印语句,该错误就会消失并且程序可以正常运行。
为什么不能在此函数中添加打印语句?
答案 0 :(得分:4)
答案是“因为类型 ”。具体来说:
isBinary :: String -> Bool
isBinary ss = do
....
由于它是do
块,因此isBinary
和某些{{的返回类型必须与Monad m => m t
的单子类型匹配。 1}}。在这里,由于m
,t
是print "" :: 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是一种语言,它鼓励程序员使用纯函数,并通过强制程序员显式标记不纯代码来使用类型系统对其进行强制。
答案 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
文档:
用于跟踪和监视执行的功能。
这些对于调查错误或性能问题很有用。它们不应该在生产代码中使用。