例如,没有自由变量的引用透明函数:
g op x y = x `op` y
现在现在是一个免费(从f
)变量op
x
和x = 1
op = (+)
f y = x `op` y
视角的函数:
f
function (y) { return x }
也是参考透明的。但它是纯粹的功能吗?
如果它不是纯函数,那么引用透明的函数的名称是什么,但是使用一个或多个绑定在封闭范围内的变量?
这个问题的动机:
我不清楚Wikipedia的文章:
结果值不需要依赖于所有(或任何)参数值。 但是,它必须仅依赖于参数值。
(强调我的)
也不从Google搜索纯函数是否可以依赖于free(在封闭范围中绑定的意义上,而不是在函数范围内绑定)变量。
此外,this book说:
如果没有自由变量的函数是纯粹的,那么闭包是不纯的吗?
函数
function (y) { return x }
很有意思。它包含一个 自由变量,x。自由变量是未绑定的变量 功能。到目前为止,我们只看到了一种“绑定”变量的方法, 即通过传递具有相同名称的参数。自从 函数{{1}}没有名为x的参数, 变量x未绑定在此函数中,这使其“自由”。现在我们知道函数中使用的变量是绑定的还是 免费,我们可以将函数分为具有自由变量和 那些没有:
- 不包含自由变量的函数称为纯函数。
- 包含一个或多个自由变量的函数称为闭包。
那么“纯函数”的定义是什么?
答案 0 :(得分:17)
尽我所知"纯度"是在语义层面上定义的,而在" referentially transparent"可以在语法和嵌入lambda演算替换规则中具有意义。定义任何一个也会带来一些挑战,因为我们需要有一个强大的程序平等概念,这可能具有挑战性。最后,重要的是要注意,自由变量的概念完全是语法的 - 一旦你进入了一个价值领域,你就不能再拥有带有自由变量的表达式 - 它们必须被束缚在其他的那个&#39 ; sa语法错误。
但是,让我们深入了解,看看这是否变得更加明确。
我们可以非常广泛地将引用透明度定义为语法上下文的属性。根据原始定义,这将从像
这样的句子构建New York is an American city.
我们已经挖了一个洞
_ is an American city.
如果给出两个句子片段同时引用"那么这样一个多孔的句子,一个" context"被认为是引用透明的。同样的事情,用这两者中的任何一个填充上下文并没有改变它的含义。
要清楚,我们可以选择的两个具有相同参考的片段将是"纽约"和#34;大苹果"。注入我们写的那些片段
New York is an American city.
The Big Apple is an American city.
暗示
_ is an American city.
引用透明。为了演示典型的反例,我们可以写
"The Big Apple" is an apple-themed epithet referring to New York.
并考虑上下文
"_" is an apple-themed epithet referring to New York.
现在当我们注入两个引用相同的短语时,我们得到一个有效和一个无效的句子
"The Big Apple" is an apple-themed epithet referring to New York.
"New York" is an apple-themed epithet referring to New York.
换句话说,引用打破了引用透明度。我们可以通过使句子引用句法结构而不是纯粹的构造的含义来看到这是如何发生的。这个概念将在稍后返回。
令人困惑的是,上面的引用透明度定义直接适用于英语句子,我们通过字面剥离单词来构建语境。虽然我们可以用编程语言来做这件事,并考虑这样的上下文是否具有参考透明度,但我们也可能认识到这种“替代”的概念。对于计算机语言的概念至关重要。
所以,让我们清楚一点:我们可以考虑两种关于lambda演算的引用透明度 - 语法和语义。句法要求我们定义" contexts"作为用编程语言编写的文字中的漏洞。这让我们可以考虑像
这样的漏洞let x = 3 in _
并填写类似" x"等内容。我们将在稍后留下对该替代品的分析。在语义层面,我们使用lambda术语来表示上下文
\x -> x + 3 -- similar to the context "_ + 3"
并且仅限于填充不使用语法片段的孔,而是仅填充有效的语义值,应用程序执行的操作
(\x -> x + 3) 5
==>
5 + 3
==>
8
因此,当有人提到Haskell中的引用透明度时,了解他们所指的引用透明度非常重要。
在这个问题中提到哪种?由于它是关于包含自由变量的表达式的概念,我将建议它是句法的。我的推理有两个主要推动力。首先,为了将语法转换为语义,要求语法有效。在Haskell的情况下,这意味着语法有效性和成功的类型检查。但是,我们会注意到像
这样的程序片段x + 3
实际上是一个语法错误,因为x
只是未知,未绑定使我们无法将其语义视为Haskell程序。其次,变量的概念,例如可以let
- 绑定的变量(并考虑"变量&#34之间的差异;因为它指的是"槽"例如一个IORef
)完全是一个语法结构 - 甚至无法从Haskell程序的语义内部谈论它们。
因此,让我们改进问题:
包含自由变量的表达式是否(语法上)是参考透明的?
答案是,不感兴趣,没有。引用透明度是" contexts"的属性,而不是表达式。因此,让我们在上下文中探索自由变量的概念。
上下文如何有意义地拥有自由变量?它可能在洞旁边
E1 ... x ... _ ... E2
只要我们不能将某些东西插入到那个"到达的语法孔中。并在语法上影响x
然后我们就可以了。所以,例如,如果我们用
E1 ... x ... let x = 3 in E ... E2
然后我们没有"捕获" x
因此也许可以认为句法孔是引用透明的。但是,我们对语法很满意。让我们考虑一个更危险的例子
do x <- foo
let x = 3
_
return x
现在我们看到,我们在某种意义上提供的漏洞主宰了后来的短语&#34; return x
&#34;。事实上,如果我们注入像#34; let x = 4
&#34;那确实改变了整体的意义。从这个意义上说,这里的语法不是引用透明的。
引用透明度和自由变量之间的另一个有趣的交互是分配上下文的概念,如
let x = 3 in _
从外部角度来看,两个短语&#34; x
&#34;和&#34; y
&#34;引用相同的东西,一些命名变量,但
let x = 3 in x ==/== let x = 3 in y
现在,希望上一节解释了在各种句法语境下引用透明度的几种方法。值得提出更难的问题,关于什么类型的上下文是有效的以及什么类型的表达是等价的。例如,我们可能会在之前的示例中使用do
符号,最终注意到我们没有使用真正的上下文,而是使用更高阶的上下文
foo >>= \x -> (let x = 3 in ____(return x)_____)
这是一个有效的背景概念吗?这很大程度上取决于我们给予该计划的意义。消除语法的概念已经暗示语法必须足够明确,以允许这样的贬低。
作为一般规则,我们必须非常谨慎地定义上下文和平等概念。此外,我们要求我们语言的片段越多,它们就越平等,我们可以构建的有效语境越少。
最终,这导致我们一直到我所谓的语义引用透明度&#34;早些时候,我们只能将正确的值替换为正确的,闭合的lambda表达式,并且我们将得到的相等性作为程序&#34;等等。
这最终意味着当我们对我们的语言赋予越来越多的意义时,当我们开始接受越来越少的有效事物时,我们就引用透明度得到更强大和更强的保证。
所以这最终导致了纯函数的概念。我在这里的理解是(甚至)不那么完整,但值得注意的是,作为一个概念,纯度并没有太多存在,直到我们转移到一个非常丰富的语义空间--Haskell语义作为一个类别过度完成部分订单。
如果这没有多大意义,那么只要想象纯度是一个概念,只有在将Haskell值作为函数和程序的相等性时才存在。特别是,我们检查了Haskell函数的集合
trivial :: a -> ()
trivial x = x `seq` ()
我们为每个trivial
选项都有一个a
函数。我们将使用下划线
trivial_Int :: Int -> ()
trivial_Int x = x `seq` ()
现在我们可以将(非常严格的)纯函数定义为函数f :: a -> b
,以便
trivial_b . f = trivial_a
换句话说,如果我们抛弃计算函数的结果b
,那么我们也可能从未计算过它。
同样,当你的表达式包含自由变量时,没有Haskell值,也没有Haskell值的概念,因此没有纯度的概念(因为它是语法错误)。
最终,答案是你不能谈论自由变量的纯度,而且无论何时谈论语法,你都可以通过多种方式打破引用透明度。在某些时候,当您将语法表示转换为其语义表示时,您必须忘记自由变量的概念和名称,以使它们代表lambda术语的缩减语义,并且此时我们已经开始具有引用透明性。
最后,纯度比参考透明度更严格,甚至与你的(引用透明)lambda术语的缩减特性有关。
通过上面给出的纯度定义,Haskell的大多数本身并不纯粹,因为Haskell可能代表非终止。然而,许多人认为这是对纯度的更好定义,因为非终止可以被认为是计算的副作用而不是有意义的结果值。
答案 1 :(得分:3)
维基百科的定义是不完整的,只要纯函数可以使用常量来计算其答案。
当我们看
时increment n = 1+n
这是显而易见的。也许它没有被提及,因为它是显而易见的。
现在Haskell的技巧是不仅顶层值和函数是常量,而且在闭包内也变量(!)关闭:
add x = (\y -> x+y)
此处x
代表我们应用add
的值 - 我们将其称为变量,因为它可以在右侧更改 add
的{{1}},但因为每次我们应用add
时它可能会有所不同。然而,从lambda的角度来看,x
是一个常数。
因此,自由变量总是在使用它们的位置命名常量值,因此不会影响纯度。
答案 2 :(得分:1)
简短回答是是 f 是纯粹的
在Haskell map
中定义了foldr
。你同意map
是否有效?如果是这样的话,它是否具有未作为参数提供给foldr
的全局函数map
是否重要?
在map
foldr
中是一个自由变量。毫无疑问。它是一个函数或评估值的东西没有区别。它是一样的。
自由变量,如函数foldl
和+
,对函数式语言的存在至关重要。如果没有它,你就不会有抽象,语言会比Fortran更糟糕。