Haskell withFile
打开一个给定IOMode
的文件,然后应用函数Handle -> IO r
。最终,它返回一个类型IO r
。
Prelude> import System.IO
Prelude System.IO> :t withFile
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
print
获取导出a
的{{1}},然后返回Show
类型。
IO ()
Prelude System.IO> :t print
print :: Show a => a -> IO ()
和IO r
之间的显着差异是什么?
答案 0 :(得分:5)
IO
是一种类型* -> *
,也就是说它需要一个类型参数。通常,IO
表示可以执行I / O并产生结果的monadic动作。给IO
的类型参数确定结果的类型。因此,
IO ()
是一个可以执行I / O并产生()
的monadic操作。 A ()
只有一个值,所以它没有传达任何信息。由于它不传达任何信息,因此通常使用void
作为传统过程编程语言中的返回值的方式使用它。
IO r
是一个可以执行I / O并生成r
的monadic操作。您可能会注意到与上述语句的相似之处。区别在于,()
不是像r
这样的具体类型,而是类型变量。
让我详细说明这意味着什么及其后果。查看id
的类型:
ghci> :t id
id :: a -> a
这当然意味着如果id
被赋予a
类型的参数,它将返回相同类型a
的结果。现在检查const ()
的类型:
ghci> :t const ()
const () :: a -> ()
如果我们给它a
,它将返回()
类型的结果。现在检查error
:
ghci> :t error
error :: String -> a
我们必须给它一个String
,但它的返回值可以适应我们需要的任何东西。当然,因为我们不一定能构造任何给定类型的值,这意味着唯一可能的定义是从不返回一个值,这是error
所做的。
因此,有了这种理解,您应该意识到虽然IO r
总是意味着“可以执行I / O并返回类型r
的值的monadic动作”,但其含义可能会有所不同,具体取决于在类型签名中出现的位置。让我们看看你的具体例子:
ghci> :t withFile
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
如果我们有一个函数返回IO r
而其他地方没有出现r
,我们可以得出的唯一结论是IO
必须永远不会产生值,否则我们无法声称它可以返回任意r
。幸运的是,情况并非如此:确实出现了另一个r
。 withFile
采用返回IO r
的函数。由于withFile
会产生IO
来创建r
,并且它知道如何创建r
的唯一方法是通过我们提供的函数,我们知道如果它是要终止,它必须至少执行一次我们给它的功能。此外,我们知道它必须返回其中的一个r
。
因此,在withFile
的上下文中,IO r
意味着如果你给它一个函数产生一个返回某个特定类型的monadic动作,withFile
最终会给你一个产生这种类型的monadic动作也是如此。作为一个具体的例子:
myInt <- withFile "number.txt" ReadMode (fmap read . hGetContents)
print (myInt + (1 :: Int))
hGetContents
,如果Handle
,将返回IO String
。 fmap read
会将其转换为IO Int
。由于withFile
是使用类型变量定义的,未指定任何特定的具体类型,因此它将适应我们提供的类型(Handle -> IO Int
)并返回IO Int
。然后我们可以在<-
符号块中使用do
来执行它并将myInt
绑定到结果。返回值通过withFile
冒充。
答案 1 :(得分:2)
IO r
表示在执行具有副作用的操作后,其中将包含一些值r
。
IO
的 ()
次操作可与其他语言的void
进行比较。 (实际上它是一个空元组()
,其类型也是()
)。
IO r
和IO ()
之间的区别在于,在执行具有副作用的操作后的第二种情况下,它不包含任何值。这表示您正在执行IO
操作,其唯一目的是打印到屏幕等副作用。而在IO r
中,在执行具有副作用的操作后,它还包含一些包含在IO
中的值,该值随后可以在您的程序中使用。
即使withFile
函数也可以返回IO ()
,因为r
只是()
的更一般形式,具体取决于传递给它的高阶函数的类型:
test = withFile "testFile" ReadMode (\handle ->
do
str <- hGetContents handle
print str
)
检查其类型:
ghci> :t test
test :: IO ()
答案 2 :(得分:0)
IO r
是一种更通用的类型,然后是IO ()
。
r
是一个变量,可以是任何类型。
如果r == ()
,则IO r == IO ()