什么是参照透明度?

参考透明度这个术语是什么意思? 我听说它被形容为“这意味着你可以用等于代替等于”,但这似乎是一个不适当的解释。

“参照透明度”这个术语来自分析哲学 ,它是基于逻辑和math方法分析自然语言结构,陈述和论点的哲学分支。 换句话说,它是我们所谓的编程语言语义学之外最接近计算机科学的学科。 哲学家奎因负责启动参照透明度的概念,但也隐含在伯特兰罗素和阿尔弗雷德怀特黑德的方法。

“参照透明度”的核心是一个非常简单明了的概念。 “指涉”这个词在分析哲学中用来谈论一个expression所指的事物 。 它与我们在编程语言语义中的“意义”或“外延”意思大致相同。 使用安德鲁·伯克特(Andrew Birkett)的例子( 博客文章 ),“苏格兰首都”一词指的是爱丁堡市。 这是一个“指涉”的简单例子。

如果用另一个涉及同一个实体的术语replace这个上下文中的术语,并不改变其含义,则句子中的上下文是“引用透明的”。 例如

苏格兰议会在苏格兰首府举行会议。

的意思是一样的

苏格兰议会在爱丁堡举行会议。

所以“苏格兰议会在……会面”的背景是一个透明的背景。 我们可以用“爱丁堡”来代替“苏格兰的首府”而不会改变意思。 换一种说法,上下文只关心这个术语是指什么,别的什么。 这就是上下文“引用透明”的意义。

另一方面,在句子中,

爱丁堡自1999年以来一直是苏格兰的首府。

我们不能做这样的replace。 如果我们这样做,我们会得到“爱丁堡自1999年以来一直是爱丁堡”,这是一个可笑的东西说,并没有传达与原来的句子相同的意思。 因此,似乎“爱丁堡自1999年以来……”的背景似乎是不透明的(与之相对的是透明的)。 它显然关心的东西比这个词所指的更多 。 它是什么?

诸如“苏格兰首都”之类的东西被称为明确的术语 ,长期以来他们对逻辑学家和哲学家都没有多less头痛。 罗素和奎因把它们排除在外,说它们实际上不是“指称的”,也就是说,认为上述例子被用来指代实体是错误的。 了解上述句子的正确方法就是说

苏格兰自1999年以来有了一个首都,首都是爱丁堡。

这句话不能转化为一个坚强的人。 问题解决了! 奎因的观点是说自然语言是混乱的,或者说至less是复杂的,因为它是便于实际使用的,而哲学家和逻辑学家则应该以正确的方式理解它们。 引用透明度是用来使这种意义明确的工具。

这与编程有什么关系? 其实不是很多。 正如我们所说的,参照透明是一种用于理解语言的工具,即分配意义 。 创build了编程语言语义学领域的克里斯托弗·斯特拉奇 ( Christopher Strachey)在他的意义研究中使用了它。 他的基础论文“ 编程语言的基本概念 ”可以在网上find。 这是一个漂亮的文件,每个人都可以阅读和理解它。 所以,请这样做。 你会很开明。 他在这一段中引入了“参照透明度”一词:

expression式最有用的属性之一就是由Quine [4]引用的透明度。 实际上,这意味着如果我们想要find包含子expression式的expression式的值,那么我们需要知道的子expression式的唯一值就是它的值。 子expression式的任何其他特征,例如其内部结构,其组成部分的数量和性质,评估顺序或写入墨水的颜色,与主体的价值无关expression。

“本质上”的使用表明,斯特拉奇用这个简单的术语来解释它。 function程序员似乎以自己的方式理解这个段落。 论文中还有9个“参照透明”事件,但似乎没有任何其他的问题。 实际上,Strachey的整篇文章都致力于解释命令式编程语言的含义。 但是,今天,function程序员声称,命令式编程语言不是透明的。 Strachey将转入他的坟墓。

我们可以挽救这个局面。 我们说自然语言是“杂乱的,至less是复杂的”,因为它是为了方便实际使用而制作的。 编程语言也是一样的。 它们是“杂乱的,或者至less是复杂的”,因为它们使得它们便于实际使用。 这并不意味着他们需要混淆我们。 他们只能用正确的方式来理解,使用一种相对透明的元语言,这样我们才能有清晰的意义。 在我引用的论文中,斯特拉西完全是这样做的。 他通过将命令式编程语言分解为基本概念来解释命令式编程语言的含义,从不会在任何地方失去清晰度 他的分析的一个重要部分是指出编程语言中的variables有两种“值”,称为l值r值 。 在Strachey的论文之前,这个问题并没有被理解,混淆至上。 今天,C的定义经常提到,每个C程序员都明白这个区别。 (其他语言的程序员是否同样理解这一点很难说。)

Quine和Strachey都关心涉及某种forms的语境依赖的语言结构的含义。 例如,我们的例子“爱丁堡自1999年以来一直是苏格兰的首都”标志着“苏格兰首都”取决于考虑时间的事实。 在自然语言和编程语言中,这样的上下文依赖是现实的。 即使在函数式编程中,自由和绑定variables也要根据它们出现的上下文来解释。任何types的上下文依赖都会以某种方式阻止参考透明。 如果你试图理解术语的含义,而不考虑它们所依赖的上下文,那么你最终会感到困惑。 奎因关心的是模态逻辑的含义。 他认为模式逻辑是不透明的,应该通过将其转化为一个引用透明的框架(例如,将必要性视为可certificate性)来清理。 他很大程度上失去了这个辩论 逻辑学家和哲学家都认为克里普克可能的世界语义是完全适合的。 类似的情况也符合命令式编程。 Strachey解释的状态依赖和Reynolds解释的存储依赖(类似于Kripke可能的世界语义)是完全足够的。 function程序员对这项研究并不了解。 他们关于参照透明度的想法要用大量的盐来进行。

[附加说明:上面的例子说明了一个简单的短语,比如“苏格兰首都”,有多层含义。 在一个层面上,我们可能在谈论当前的资本。 在另一个层面上,我们可能会谈论苏格兰可能经历的所有可能的首都。 在正常的实践中,我们可以“放大”一个特定的上下文,并“放大”以便很容易地跨越所有上下文。 自然语言的效率利用了我们的能力。 命令式编程语言在很大程度上是非常有效的。 我们可以在赋值( r值 )的右边使用variablesx来讨论它在特定状态下的值。 或者,我们可以谈谈它横跨所有州的l值 。 人们很less被这样的事情困惑。 但是,他们可能或者不可能精确地解释语言结构中固有的所有层面的意义。 所有这些层面的意义不一定是“显而易见的”,而是恰当地研究它们是科学的问题。 然而,普通百姓解释这种分层意义的不合理并不意味着他们对此感到困惑。

下面的一个单独的“后记”将这个讨论与function和命令编程的关注联系起来

引用透明是函数式编程中常用的一个术语,意思是给定一个函数和一个input值,你将总是得到相同的输出。 也就是说在函数中没有使用外部状态。

这是一个引用透明函数的例子:

int plusOne(int x) { return x+1; } 

使用引用透明函数给定一个input和一个函数,可以用一个值代替它,而不是调用该函数。 所以,不要用5来调用plusOne,我们可以用6replace它。

另一个很好的例子是一般的math。 在给定一个函数和一个input值的math中,它总是映射到相同的输出值。 f(x)= x + 1因此,math中的函数是透明的。

这个概念对于研究人员来说很重要,因为这意味着当你有一个引用透明的函数时,它就可以轻松地自动并行化和caching。

引用透明性总是在像Haskell这样的函数式语言中使用。

相反,存在指称不透明的概念。 这意味着相反。 调用函数可能并不总是产生相同的输出。

 //global G int G = 10; int plusG(int x) {//G can be modified externally returning different values. return x + G; } 

另一个例子是面向对象编程语言中的成员函数。 成员函数通常对其成员variables进行操作,因此将是不透明的。 会员function当然可以是透明的。

又一个例子是从文本文件中读取并打印输出的函数。 这个外部文本文件可能会随时改变,所以这个函数是不透明的。

[这是我3月25日回答的后记,旨在使讨论更接近function/命令式编程的关注。]

function程序员的参照透明思想似乎与三个方面的标准概念不同:

  • 鉴于哲学家/逻辑学家使用“参考”,“外延”,“指定”和“ 理论 ”(弗雷格的德语术语)等术语,function程序员使用术语“价值”。 (这不完全是他们的行为,我注意到Landin,Strachey及其后代也使用“价值”这个词来谈论引用/外延,这可能只是Landin和Strachey所引入的术语简化,当以一种天真的方式使用时有很大的不同。)

  • function程序员似乎相信这些“价值”存在于编程语言中,而不是外部。 这样做,他们不同于哲学家和编程语言语义学家。

  • 他们似乎认为这些“价值”应该是通过评估来获得的。

例如,维基百科有关参照透明度的文章今天上午说:

如果一个expression式在不改变程序的行为的情况下(换句话说,产生一个具有相同效果和相同input的输出的程序),就可以用它的值代替expression式。

这与哲学家/逻辑学家所说的完全不一样。 他们认为,如果上下文中的expression可以被指向同一事物的另一个expression (一种共同的expression)所替代,那么上下文就是指引的或者是引用透明的。 这些哲学家/逻辑学家是谁? 他们包括弗雷格 , 罗素 , 怀特黑德 , 卡尔纳普 , 奎因 , 教会和无数其他人。 他们每个人都是高耸的身材。 这些逻辑学家的综合智力至less可以说是惊天动地。 他们都一致认为,在正式语言之外存在指称/指称的地方,语言中的expression只能谈论它们。 所以,在语言中所能做的就是用另一个引用同一个实体的expression来replace一个expression式。 语言中存在指称/指示。 为什么function程序员偏离这个既定的传统?

人们可能会认为编程语言的语义学家可能误导了他们。 但是,他们没有。

Landin :

(a)每个expression式都有一个嵌套子expression式结构,(b)每个子expression式表示一些东西(通常是数字,真值或数值函数) ,(c)expression式表示的东西,即它的“值”,只取决于它的子expression式的值,而不是它们的其他属性。 [新增重点]

大豆 :

关于expression式唯一重要的是它的价值,任何子expression式都可以被任何其他值相等的 [增加重点]替代。 而且,expression的价值在一定的限度内,每当它出现的时候都是一样的“。

鸟和Wadler :

expression式的值只取决于其构成expression式的值(如果有的话),这些子expression式可以被具有相同值其他expression式自由replace[增加重点]。

所以,现在回想起来,Landin和Strachey用“价值”取代“参考”/“外延”来简化术语的努力可能是不合理的。 一旦听到“价值”,就会想到一个导致它的评估过程。 不pipe评价如何产生“价值”,也是同样诱人的,虽然这可能是非常明显的,但这不是外延。 这就是我在function程序员眼中所发生的“参照透明”的概念。 但是早期语义学家所说的“价值” 并不是评价的结果, 也不是一个function或任何事物的输出。 这是这个词的外延。

一旦我们理解了作为一个复杂的math/概念对象的expression(在古典哲学家的话语中的“参考”或“外延”)的所谓“价值”,各种可能性就开放了。

  • Strachey将命令式编程语言中的variables解释为L值 ,正如我在3月25日的答案中提到的那样,这个复杂的概念对象在编程语言的语法中没有直接的表示。
  • 他还将语言中的命令解释为状态到状态的函数,这是语法中不是“价值”的复杂math对象的另一个实例。
  • 即使C中的一个副作用函数调用也具有一个定义良好的“值”作为状态变换器,将状态映射到状态和值对(function程序员术语中的所谓“monad”)。

function程序员不愿意将这些语言称为“引用透明的”,仅仅意味着他们不愿意接受这样复杂的math/概念对象作为“价值”。 另一方面,他们似乎完全愿意把状态变换器称为“价值”,当它被放在自己喜欢的语法中,并用“monad”这样的stream行词来打扮。 我不得不说,他们是完全不一致的,即使我们向他们表示,他们的“参照透明”思想有一定的连贯性。

有些历史可能会对这些混淆如何产生一些启示。 从1962年到1967年,克里斯托弗·斯特拉奇是非常密集的。 1962年至1965年间,他兼任莫里斯·威尔克斯(Maurice Wilkes)的研究助理,负责devise和实现被称为CPL的编程语言。 这是一个命令性的编程语言,但也意味着具有强大的函数式编程语言function。 在他的顾问公司担任斯特拉齐员工的兰丁,对斯特拉奇的编程语言观点产生了巨大的影响。 在1965年具有里程碑意义的论文“ Next 700编程语言 ”中,Landin毫无顾忌地提倡使用函数式编程语言(称之为外延语言),并将命令式编程语言描述为“对立面”。 在随后的讨论中,我们发现斯泰拉希对兰登的强势地位产生了怀疑。

DLs构成了所有语言的一个子集。 它们是一个有趣的子集,但是除非习惯它,否则不便使用。 我们需要他们,因为目前我们不知道如何用包含命令和跳跃的语言来构buildcertificate。 [新增重点]

1965年,斯特拉奇在牛津大学读了一个读者的位置,似乎基本上全职工作,发展一个必要性和跳跃的理论。 到了1967年,他已经准备好了一个理论,他在哥本哈根夏令营的“ 编程语言的基本概念 ”课程中教授了这个理论。 讲稿本应该是已经出版了,但“不幸的是,由于拖延编辑,程序没有实现,像斯特拉齐在牛津大部分的工作,然而,该文件有一个有影响的私人发行。 ( 马丁·坎贝尔 – 凯利 )

获得斯特拉奇的着作的困难可能会导致混乱的传播,人们依赖二手资料和传闻。 但是,现在“networking基本概念 ”已经可以在网上find,不需要猜测。 我们应该阅读它,并且对斯特拉奇的意思做出自己的想法。 尤其是:

  • 在3.2节中,他处理“expression式”,他谈到“R值参照透明度”。
  • 他的第3.3节涉及“L值参照透明”的“命令”。
  • 在第3.4.5节中,他谈到了“函数和例程”,并且声明“在R值上下文中R值参照透明度的任何偏离应该通过将expression式分解成几个命令和简单的expression式来消除,或者如果事实certificate,这是一个困难,评论的主题。“

任何关于“参照透明度”的讨论,如果不理解L值,R值和填充命令式程序员概念宇宙的其他复杂对象之间的区别,根本就是错误的。

引用透明的function是仅依赖于其input的function。

如果一个expression式可以用它的值来代替,而不改变algorithm,那么expression式就是透明的,在相同的input上产生具有相同效果和输出的algorithm。

一个引用透明的函数就像一个math函数。 给定相同的input,它将始终产生相同的输出。 这意味着通过的国家没有改变,而且这个职能没有自己的状态。

如果你对词源感兴趣的话(比如为什么这个概念有这个名字),看看我的博客文章 。 术语来自哲学家/逻辑学家奎因。

对于那些需要简明扼要的解释的人来说,我会冒险的(但请阅读下面的内容)。

编程语言中的参照透明度促进了等式推理 – 参照透明度越高,就越容易进行等式推理。 例如,用(伪)函数定义,

fx = x + x,

在这个定义的范围内,你可以(安全地)用foo + foo代替f(foo),而不会对你可以执行这个约减的地方有太多限制,这是你的程序devise语言具有。

例如,如果foo在C编程的意义上是x ++,那么你不能安全地执行这个减less(也就是说,如果你执行这个减less,你将不会得到与你开始的程序相同的程序)。

在实际的编程语言中,你不会看到完美的参考透明性,但function程序员比大多数人更关心它(参见Haskell,它是一个核心目标)。

(完全披露:我是一个function强大的程序员,所以最好的答案是你应该用一点盐来解释这个问题。)

  1. 指称语义是build立在build模语言基础上的,构build可构成指定值的域。
  2. function程序员使用术语来描述基于语言重写规则的计算收敛。 其操作语义。

1中有两种语言的清晰度:

  • 被build模的对象语言
  • 元语言的build模语言

2,由于对象和元语言的接近性,可能会感到困惑。

作为语言实施者,我发现我需要不断地记住这个区别。

所以雷迪教授可以这样解释一下:-)

在函数式编程和语义上下文中,术语“ 参照透明”并不是透明的。

请注意,这个“含义”的概念是在观察者的脑海中发生的事情。 因此,同一个“参照”对不同的人来说可能意味着不同的东西。 例如,我们在维基百科有一个爱丁堡消歧义页面。

在编程环境中可能出现的相关问题可能是多态。

也许我们应该为多态性的特殊情况(或者甚至是铸造)命名,在这种情况下,对于我们的目的而言,不同的多态情况在语义上是等价的(而不仅仅是类似的情况)。例如,数字1–可以表示使用整数types,或复杂types或任何其他types – 可以多态处理)。

下面的答案,我希望增加和限定有争议的第一和第三个答案。

让我们授予一个expression式表示或引用某个指示对象。 然而,一个问题是,这些指称是否可以作为expression式本身的一部分被同构地编码,称为这种expression的“价值”。 例如,文字数字值是算术expression式集合的一个子集,真值是布尔expression式集合的一个子集,等等。这个想法是对一个expression式求值(如果它有一个)。 所以“价值”这个词可能指的是一组expression式中的一个表示或一个显着的元素。 但是,如果在所指对象和价值之间存在同构(双射),我们可以说它们是同一个东西。 (这就是说,我们必须小心地定义指称和同构,如指称语义领域所certificate的那样,通过回答第三个答案来提出一个例子,代数数据types定义data Nat = Zero | Suc Nat不符合预期的那组自然数。)

让我们写一个有孔的expression式E[·] ,在某些方面也被称为“上下文”。 C类expression式的两个上下文示例是[·]+1[·]++

让我们为带有expression式(无孔)的函数编写[[·]] ,并在某些含义的宇宙中传递它的含义(指称,指示等)。 (我借用了指称语义领域的符号。)

让我们在某种程度上适应奎因的定义如下:如果给定任意两个expression式E1E2 (在那里没有孔)使得[[E1]] = [[E2]]则上下文E[·]在引用上是透明的(即expression式表示那么就是[[E[E1]]] = [[E[E2]]] (即用E1E2填充空洞的结果表示也表示相同的指称)。

莱布尼兹的等于等于等于的规则通常表示为“如果E1 = E2E[E1] = E[E2] ”,即E[·]是一个函数。 函数(或计算函数的程序)是从源到目标的映射,以便每个源元素至多有一个目标元素。 非确定性的function是错误的,它们要么是关系,要么是交付集合的function等等。如果在莱布尼茨的规则中,平等=是指称的,那么双括号就被认为是理所当然的。 所以一个透明的上下文是一个function。 而莱布尼兹的规则是等式推理的主要成分,因此等式推理肯定与参照透明有关。

尽pipe[[·]]是一个从expression式到指示的函数,它可能是从expression式到被理解为expression式的一个受限子集的“值”的函数, [[·]]可以理解为评估。

现在,如果E1是一个expression式,而E2是一个值,那么在expression式,值和评估方面定义参照透明度时,我们认为这是大多数人的意思。 但是,正如本页面的第一个和第三个答案所示,这是一个不确定的定义。

与诸如[·]++上下文的问题不是副作用,而是它的值不是以C的forms同义定义的。 函数不是值(好吧,指向函数),而在函数式编程语言中,它们是。 Landin,Strachey和指称语义学的开拓者在使用function世界来提供意义方面相当聪明。

对于命令式的C语言,我们可以(大致)使用函数[[·]] : Expression -> (State -> State x Value)为expression式提供语义[[·]] : Expression -> (State -> State x Value)

ValueExpression一个子集。 State包含对(标识符,值)。 语义函数接受一个expression式,并将它从当前状态转换为具有更新状态和值的对。 例如, [[x]]是从当前状态到第一个组件是当前状态,第二个组件是x的值的对的函数。 相反, [[x++]]是从当前状态到第一个分量是x值递增的状态,第二个分量是该值的状态的函数。 从这个意义上讲,如果满足上面给出的定义,上下文[·]++就是透明的。

我认为函数程序员有权使用引用透明,因为它们从expression式到值自然地将[[·]]作为函数恢复。 函数是一stream的值,状态也可以是一个值,而不是一个表示。 国家monad(部分)是一个传递(或穿过)状态的干净机制。

Interesting Posts