Haskell “print”和“withFile”的不同返回类型`
Haskell的Haskell “print”和“withFile”的不同返回类型`,haskell,Haskell,Haskell的withFile打开一个带有给定IOMode的文件,然后应用函数Handle->IO r。最后,它返回一个类型ior Prelude> import System.IO Prelude System.IO> :t withFile withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r print获取派生Show的a,然后返回一种类型的IO() IO r和IO()之间的显著区别是
withFile
打开一个带有给定IOMode
的文件,然后应用函数Handle->IO r
。最后,它返回一个类型ior
Prelude> import System.IO
Prelude System.IO> :t withFile
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
print
获取派生Show
的a
,然后返回一种类型的IO()
IO r
和IO()
之间的显著区别是什么?IO
是一种*->*
类型,也就是说它需要一个类型参数。一般来说,IO
表示一个可以执行I/O并产生结果的一元操作。给定给IO
的type参数确定结果的类型。所以,
IO()
是一种一元操作,可以执行I/O并生成一个()
一个()
只有一个值,因此它不传递任何信息。由于它不传递任何信息,因此它的使用方式通常与传统过程编程语言中使用void
作为返回值的方式相同IO r
是一种可以执行I/O并产生r
的一元操作,您可能会注意到与上述语句类似。不同之处在于,r
不是像()
那样的具体类型,而是一个类型变量id
的类型:
ghci> :t id
id :: a -> a
当然,这意味着如果给id
一个a
类型的参数,它将返回一个与a
类型相同的结果。现在检查const()
的类型:
如果我们给它一个a
,它将返回类型为()
的结果。现在检查错误:
ghci> :t error
error :: String -> a
我们必须给它一个字符串
,但它的返回值可以适应我们需要的任何内容。当然,由于我们不必构造任何给定类型的值,这意味着唯一可能的定义是永远不返回值,这就是error
所做的
因此,有了这样的理解,您应该认识到,虽然IO r
始终意味着“可以执行I/O并返回r
类型值的一元操作”,但其含义可能会因其在类型签名中的位置而异。让我们看一下您的特定示例:
ghci> :t withFile
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
如果我们有一个函数返回一个IO r
,而没有r
出现在其他任何地方,那么我们可以得出的唯一结论是IO
决不能产生一个值,否则我们就不能声称它可以返回任意的r
。幸运的是,情况并非如此:另一个r
确实出现了withFile
接受一个返回IO r
的函数。由于withFile
生成一个创建r
的IO
,并且它知道如何创建r
的唯一方法是通过我们给它的函数,我们知道如果它要终止,它必须至少执行一次我们给它的函数。此外,我们知道它必须返回它从中得到的r
s之一
因此,在withFile
的上下文中,ior
意味着,如果给它一个函数,该函数产生一个返回特定类型的一元操作,withFile
也会给你一个产生该类型的一元操作。作为一个具体例子:
myInt <- withFile "number.txt" ReadMode (fmap read . hGetContents)
print (myInt + (1 :: Int))
myInt-IO-Int
)并返回一个IO-Int
。然后我们可以使用IO
是一种*->*
类型,也就是说它需要一个类型参数。一般来说,IO
表示一个可以执行I/O并产生结果的一元操作。给定给IO
的type参数确定结果的类型。所以,
IO()
是一种一元操作,可以执行I/O并生成一个()
一个()
只有一个值,因此它不传递任何信息。由于它不传递任何信息,因此它的使用方式通常与传统过程编程语言中使用void
作为返回值的方式相同
IO r
是一种可以执行I/O并产生r
的一元操作,您可能会注意到与上述语句类似。不同之处在于,r
不是像()
那样的具体类型,而是一个类型变量
让我进一步阐述这意味着什么以及由此产生的后果。查看id
的类型:
ghci> :t id
id :: a -> a
当然,这意味着如果给id
一个a
类型的参数,它将返回一个与a
类型相同的结果。现在检查const()
的类型:
如果我们给它一个a
,它将返回类型为()
的结果。现在检查错误:
ghci> :t error
error :: String -> a
我们必须给它一个字符串
,但它的返回值可以适应我们需要的任何内容。当然,由于我们不必构造任何给定类型的值,这意味着唯一可能的定义是永远不返回值,这就是error
所做的
因此,有了这样的理解,您应该认识到,虽然IO r
始终意味着“可以执行I/O并返回r
类型值的一元操作”,但其含义可能会因其在类型签名中的位置而异。让我们看一下您的特定示例:
ghci> :t withFile
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
如果我们有一个函数返回一个IO r
,而没有r
出现在其他任何地方,那么我们可以得出的唯一结论是IO
决不能产生一个值,否则我们就不能声称它可以返回任意的r
。幸运的是,情况并非如此:另一个r
确实出现了withFile
使用一个函数
ghci> :t test
test :: IO ()