如果你编译一个没有输入的程序会怎么样?(Haskell IO纯度问题(再次))

The*_*kle 7 haskell functional-programming side-effects referential-transparency

putStrLn当使用任何参数调用时,将始终返回type的值IO ().我同意这是纯粹的,我可以处理.但它是否具有参考透明度?我是这么认为的,因为对于任何给定的输入,你可以用一个IO ()将在stdout抛出正确字符串的函数调用替换.

所以我很酷putStrLn,但是getLine当没有参数调用时可以返回任何数量的东西,只要它们是类型的IO String.这既不是纯粹的,也不是引用透明的吗?

愚蠢的迂腐问题,它可能不会改变我编写代码的方式,但我真的想要一劳永逸地解决这个问题.(我知道IO monad会正确排序,这不是我的问题)

这为我提出了另一个问题.编译器是否足够智能以识别不带输入的程序?比如说我编译

main = putStrLn . show $ map (+1) [1..10]
Run Code Online (Sandbox Code Playgroud)

GHC是否足够聪明,可以将该程序减少到IO ()导致[2,3,4,5,6,7,8,9,10,11]打印出来的程序?或者它仍在运行并在运行时评估/执行所有内容?对于不需要输入的任意程序也是如此.GHC是否采用了这样一个事实:整个程序是透明的,并且可以简单地用它的价值取代?

Nat*_*ell 7

我想这里有两个问题.

  1. IO引用是否透明
  2. GHC会在编译时减少任意表达式吗?

查看IO的类型,您可以想象它依赖于RealWorld没有数据构造函数的神秘值,并通过使每个语句依赖于最后一个(在单个线程世界中)来模拟引用透明性.在a的情况下IO String,这是一个newtype包装器RealWorld -> (RealWorld, String)...这是一个函数,而不是一个值.在IO没有Monad实例的情况下使用会使这一点特别且痛苦地显而易见.

Prelude GHC.Types> :info IO
newtype IO a
  = IO (GHC.Prim.State# GHC.Prim.RealWorld
        -> (# GHC.Prim.State# GHC.Prim.RealWorld, a #))
Run Code Online (Sandbox Code Playgroud)

至于GHC的优化,在这种情况下,它不会在编译时将列表减少为字符串.GHC 7.2.1生成的优化代码懒洋洋地生成一个列表,在结果上映射(+1),将列表转换为字符串,最后将其打印到控制台.几乎与它在您的示例中读取的完全相同.


hug*_*omg 7

是的,这些monadic函数是纯粹的引用透明的,因为替换规则仍然适用于它们.

在Haskell中,以下两个程序是等效的

main = (puStrLn "17" >> puStrLn "17")
main = let x = putStrLn "17" in (x >> x)
Run Code Online (Sandbox Code Playgroud)

在"正常"语言中,第二个例子只打印一次,作为评估的副作用x.当你意识到类型的值IO()实际上不是副作用计算但实际上是对这种计算描述时,两个程序实际上是相同的方式变得更加清晰,你可以使用它作为构建块来构建更大的计算.


Pra*_*eek 6

getLine :: IO String是纯洁的; 它的值是IO动作,它从标准输入读取并返回*一个字符串.getLine总是等于这个值.

*由于缺少更好的词,我在这里使用"返回"这个词.

维基百科将参照透明度定义为:

如果表达式可以替换为其值而不改变程序的行为(换句话说,产生在相同输入上具有相同效果和输出的程序),则表示该表达式是引用透明的.

所以getLine也是引用透明的.虽然我想不出以一种其他方式表达其"价值"的好方法,以"用它的价值取代表达".

另外,对于诸如" putStrLn当使用任何参数调用时将始终返回IO ()"之类的语句,应该有点小心.IO ()是一种类型,而不是一种价值.对于每一个s :: String,putStrLn s都是类型的值IO (),是的.但是,这个价值s当然取决于.

(此外,如果你排除这些unsafe东西,一切都是纯粹的和引用透明的,尤其是那样getLine.)