为什么在Haskell中不将访问System.Info视为IO操作?


25

在模块中,System.Info我看到以下功能:

os :: String
arch :: String
compilerName :: String
compilerVersion :: Version

为什么没有IO那里?他们正在访问系统...我错了吗?我的期望是:

os :: IO String
arch :: IO String
compilerName :: IO String
compilerVersion :: IO Version

用例:

      print os            -- "darwin"
      print arch          -- "x86_64"
      print compilerName  -- "ghc"

Answers:


29

您在运行时没有得到该信息。它们在系统中安装时在编译器中进行了硬编码。

如果你看一下定义,这是最明显的compilerName,如发现http://hackage.haskell.org/package/base-4.12.0.0/docs/src/System.Info.html

compilerName :: String
compilerName = "ghc"

但即使像 os

os :: String
os = HOST_OS

可以使用未定义的名称HOST_OS(以大写字母??开头的值)来定义,这表示只是一个占位符,在安装过程中会被替换。

有人也可以纠正我(请!),但是该{-# LANGUAGE CPP #-}文件顶部的编译指示建议HOST_OS在编译之前用C预处理程序将类似的字符串替换为适当的字符串。


2
如果OP确实确实希望IO在其中存在某些内容,uname(3)可在Hackage上找到一个包装:hackage.haskell.org/package/bindings-uname
thsutton

19

这个问题是一个好问题。答案是这样的:每个程序编译这些值都是静态的。它们本质上已编译到程序中,此后再也不会更改。因此,如果将它们视为常量,则任何东西(在GHC使用的假设下)都不会中断。而且,使用简单的常量比执行IO操作更为方便。

但这就是所有的遗留推理。Haskell是一门古老的语言。(不,它比Java早了几年。)已经建立了许多推理不再被视为最佳实践的库。这些就是例子。暴露它们的现代库很可能会使它们执行IO操作,即使编译后结果不变。将不是源常量的东西放在IO操作后面会更有用,尽管仍然存在一些值得注意的例外,例如Int在32位和64位平台之间更改大小。

无论如何...我想说的是您的期望是坚定的,而这些类型是历史古怪的结果。


-9

编辑:感谢@interjay和@Antal Spector-Zabusky解释了为什么这个答案被否决。他们写

该文档有点误导。这些值被硬编码到GHC编译器中。在48年之后,您肯定知道实际的代码总是比文档重要。–昨天interjay @ andy256您完全正确地认为文档很糟糕(实际上,这就是Francisco首先问这个问题的原因),并且您的困惑是可以理解的。Haskell的问题是,如果这些String值可以在运行时更改,那将是一个严重的错误-不允许更改变量。这就是IO类型构造函数的意义-它表示一种允许访问“外部世界”的计算,因此其结果可以更改。进行系统调用是IO操作的一个很好的例子。…[1/2] – 9小时前Antal Spector-Zabusky @ andy256…(另一个IO操作可能是“更新全局计数器”。)因此,当我们看到String时,我们知道它不能与任何字符串进行任何通信。幕后的操作系统。这就是为什么,也许令人惊讶的是,如果您不习惯Haskell,实现os :: String来进行系统调用并不容易–任何这样的值在基本的Haskell中都是无法实现的,将违反每个程序员对程序的期望工作,甚至可能使编译器和优化器崩溃(从理论上讲不是–堆栈溢出问题使人们遇到类似问题)。[2/2] – Antal Spector-Zabusky 这就是为什么,也许令人惊讶的是,如果您不习惯Haskell,实现os :: String来进行系统调用并不容易–任何这样的值在基本的Haskell中都是无法实现的,将违反每个程序员对程序的期望工作,甚至可能使编译器和优化器崩溃(从理论上讲不是–堆栈溢出问题使人们遇到类似问题)。[2/2] – Antal Spector-Zabusky 这就是为什么,也许令人惊讶的是,如果您不习惯Haskell,实现os :: String来进行系统调用并不容易–任何这样的值在基本的Haskell中都是无法实现的,将违反每个程序员对程序的期望工作,甚至可能使编译器和优化器崩溃(从理论上讲不是–堆栈溢出问题使人们遇到类似问题)。[2/2] – Antal Spector-Zabusky 甚至可能使编译器和优化器崩溃(从理论上讲不是这样-堆栈溢出问题使人们遇到类似问题)。[2/2] – Antal Spector-Zabusky 甚至可能使编译器和优化器崩溃(从理论上讲不是这样-堆栈溢出问题使人们遇到类似问题)。[2/2] – Antal Spector-Zabusky

目前有两个删除票。我将顺其自然,但是建议它实际上具有一定的价值。从侧面说,他们的解释表明问题很薄弱,答案也很薄弱,因为Haskell新手可以轻松地遵循我的推理。

原始答案:

我不是Haskell程序员,但是已经给出的两个答案与OP链接的文档不匹配。

我对文档的解释如下。

os :: String -这将为您提供“正在运行程序的操作系统”。

我希望这将发出系统调用以获取信息。因为在其上编译程序的系统可能与在其上运行的系统不同,所以它不能是编译器插入的值。如果正在解释代码,则解释器可以提供结果,该结果必须通过系统调用获得。

arch :: String -这将为您提供“正在运行程序的计算机体系结构”。

同样,我希望这将发出系统调用以获取信息。因为在其上编译程序的系统可能与在其上运行的系统不同,所以它不能是编译器插入的值。

compilerName :: String -这将为您提供“用于编译或正在解释程序的Haskell实现”。

该值肯定是由编译器/解释器插入的。

compilerVersion :: String-这将为您提供“ compilerName用于编译或正在解释程序的版本”。

该值肯定是由编译器/解释器插入的。

尽管您可能认为前两个调用正在获取输入,但结果来自操作系统保留的值。I / O通常是指辅助存储访问。


3
对于haskell而言并非如此。在这里,任何计算都是无序的,其结果可以被缓存。函数是纯函数,因此如果函数不接受args,则它很像常量。一个arg的函数看起来就像是哈希图或字典,它们基于键来计算值。您不能使用外部环境,不能在此类函数中执行系统调用,甚至不能获得随机数或当前日期。但是,如果您实际上想使用该“排序”或环境,则需要使用IOmonad来模拟状态,来模拟操作序列
Yuri Kovalenko

“您不能使用外部环境,请在此类函数中执行syscalls” –当然可以,尤其是如果“ you”是Haskell编译器!Haskell实现很容易实现,os :: String以便在评估时执行系统调用。
Tanner Swett

2
我认为您不了解Haskell中IO monad的重要性。
Sneftel

@Sneftel您当然是正确的。我之所以选择回答,是因为在每种范例中进行了48年的编程并编写了奇数个编译器后,最初的答案与文档不符,但仍然不匹配。它清楚地说了osarch是在运行时获得的。
andy256 '19

1
该文档有点误导。这些值被硬编码到GHC编译器中。48年之后,您肯定会知道实际代码始终胜过文档。
interjay
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.