什么是参考透明度?
引用透明度一词是什么意思? 我听说它被描述为“这意味着你可以用 equals 替换 equals”,但这似乎是一个不充分的解释。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
引用透明度一词是什么意思? 我听说它被描述为“这意味着你可以用 equals 替换 equals”,但这似乎是一个不充分的解释。
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(16)
当我阅读已接受的答案时,我以为我在不同的页面而不是 stackoverflow 上。
引用透明性是定义纯函数的更正式的方式。 因此,如果一个函数在相同的输入上始终产生相同的结果,则称其为引用透明。
这不是引用透明的,因为返回值取决于外部变量“计数器”并且它不断变化。
这就是我们如何使其引用透明:
现在这个函数是稳定的,并且在提供相同的输入时始终返回相同的输出。
When I read the accepted answer, I thought I was on a different page not on stackoverflow.
Referential transparency is a more formal way of defining a pure function. Hence, if a function consistently yields the same result on the same input, it’s said to be referentially transparent.
this is not referentially transparent because return value depends on the external variable "counter" and it keeps changing.
This is how we make it referential transparent:
Now this function is stable and always returns the same output when provided with the same input.
我希望以下答案能够补充并限定有争议的第一和第三
答案。
让我们假设一个表达式表示或指代
一些参考物。 然而,问题是这些所指对象是否可以被同构编码为表达式本身的一部分,并将此类表达式称为“值”。 例如,文字数值是算术表达式集的子集,真值是布尔表达式集的子集,等等。这个想法是将表达式计算为其值(如果它有一个值)。 因此,“值”一词可以指代表达式组中的一个表示或一个显着元素。 但是,如果所指对象和值之间存在同构(双射),我们
可以说它们是同一件事。 (这就是说,必须小心地定义
指称和同构,如指称领域所证明的
语义。 举一个第三个答案的回复中提到的例子,
代数数据类型定义
data Nat = Zero | Suc Nat 没有
正如预期的那样对应于自然数集。)
让我们为带有空洞的表达式编写
E[·]
,这在某些方面也众所周知作为“上下文”。 类 C 表达式的两个上下文示例是
[·]+1
和<代码>[·]++。
让我们为接受表达式的函数编写
[[·]]
(没有漏洞)并以某种方式表达其含义(指称、指称等)
提供意义的宇宙。 (我从现场借用了符号
指称语义。)
让我们稍微正式地调整蒯因的定义如下:上下文
E[·]
是引用透明的当且仅当给定任意两个表达式
E1
和E2
(没有漏洞那里)使得
[[E1]] = [[E2]]
(即表达式表示/引用相同的指称),则情况是
[[E[E1]]] = [[E[E2]]]
(即填写具有
E1
或E2
的孔会产生也表示相同含义的表达式参考对象)。
莱布尼茨用 equals 代替 equals 的规则通常表达为“如果
E1 = E2
然后E[E1] = E[E2]
',这表示E[·]
是一个函数。 一个功能(或者就此而言,计算该函数的程序)是来自
源到目标,以便每个源最多有一个目标元素
元素。 非确定性函数是用词不当,它们要么是关系,
传递集合等的函数。如果在莱布尼茨规则中等式
=
是那么双括号就被认为是理所当然的,
省略了。 因此,引用透明上下文是一个函数。 而莱布尼茨法则是等式推理的主要成分,因此等式推理肯定与参照透明性有关。
虽然
[[·]]
是一个从表达式到表示的函数,但它也可以是一个从表达式到“值”的函数被理解为受限子集
表达式,
[[·]]
可以理解为求值。现在,如果
E1
是一个表达式,E2
是一个值,我们就得到了我认为大多数人在定义表达式、值和求值方面的引用透明度时的含义。 但正如本页的第一个和第三个答案所示,这是一个不准确的定义。[·]++
等上下文的问题不是副作用,而是它的值在 C 中没有与其含义同构地定义。 功能有不是值(好吧,函数指针是),而在函数式编程语言中它们是。 着陆,
Strachey 和指称语义学的先驱们在以下方面非常聪明:
使用功能世界来提供意义。
对于命令式 C 语言,我们可以(粗略地)提供语义
使用函数
[[·]] 的表达式:表达式 -> (状态 -> 状态 x 值)
。Value
是Expression
的子集。State
包含对(标识符,值)。 语义函数接受一个表达式并传递为
它的含义是从当前状态到具有更新的状态对的函数
状态和值。 例如,
[[x]]
是当前状态的函数到第一个分量是当前状态且第二个分量的对
分量是 x 的值。 相反,
[[x++]]
是来自其第一个分量是值的状态的对的当前状态
x 的值递增,其第二个分量就是该值。 在这个
从某种意义上说,上下文
[·]++
是引用透明的,当且仅当它满足上面给出的定义。
我认为函数式程序员有权在以下方面使用引用透明度
他们自然地将
[[·]]
恢复为从表达式到值的函数。函数是一等值,状态也可以是一个值,而不是一个
表示。 状态单子(部分)是一个干净的机制,用于传递(或
线程)状态。
The following answer I hope adds to and qualifies the controversial 1st and 3rd
answers.
Let us grant that an expression denotes or refers to
some referent. However, a question is whether these referents can be encoded isomorphically as part of expressions themselves, calling such expressions 'values'. For example, literal number values are a subset of the set of arithmetic expressions, truth values are a subset of the set of boolean expressions, etc. The idea is to evaluate an expression to its value (if it has one). So the word 'value' may refer to a denotation or to a distinguished element of the set of expressions. But if there is an isomorphism (a bijection) between the referent and the value we
can say they are the same thing. (This said, one must be careful to define
the referents and the isomorphism, as proven by the field of denotational
semantics. To put an example mentioned by replies to the 3rd answer, the
algebraic data type definition
data Nat = Zero | Suc Nat
does notcorrespond as expected to the set of natural numbers.)
Let us write
E[·]
for an expression with a hole, also known in some quartersas a 'context'. Two context examples for C-like expressions are
[·]+1
and[·]++
.Let us write
[[·]]
for the function that takes an expression (with no hole)and delivers its meaning (referent, denotation, etc.) in some
meaning-providing universe. (I'm borrowing notation from the field
of denotational semantics.)
Let us adapt Quine's definition somewhat formally as follows: a context
E[·]
is referentially transparent iff given any two expressions
E1
andE2
(no holesthere) such that
[[E1]] = [[E2]]
(i.e. the expressions denote/refer-to thesame referent) then it is the case that
[[E[E1]]] = [[E[E2]]]
(i.e. filling-inthe hole with either
E1
orE2
results in expressions that also denote the samereferent).
Leibniz's rule of substituting equals for equals is typically expressed as 'if
E1 = E2
thenE[E1] = E[E2]
', which says thatE[·]
is a function. A function(or for that matter a program computing the function) is a mapping from a
source to a target so that there is at most one target element for each source
element. Non-deterministic functions are misnomers, they are either relations,
functions delivering sets, etc. If in Leibniz's rule the equality
=
isdenotational then the double-brackets are simply taken for granted and
elided. So a referentially transparent context is a function. And Leibniz's rule is the main ingredient of equational reasoning, so equational reasoning is definitely related to referential transparency.
Although
[[·]]
is a function from expressions to denotations, it could be afunction from expressions to 'values' understood as a restricted subset of
expressions, and
[[·]]
can be understood as evaluation.Now, if
E1
is an expression andE2
is a value we have what I think is meant by most people when defining referential transparency in terms of expressions, values, and evaluation. But as illustrated by the 1st and 3rd answers in this page, this is an inacurate definition.The problem with contexts such as
[·]++
is not the side effect, but that its value is not defined in C isomorphically to its meaning. Functions arenot values (well, pointers to functions are) whereas in functional programming languages they are. Landin,
Strachey, and the pioneers of denotational semantics were quite clever in
using functional worlds to provide meaning.
For imperative C-like languages we can (roughly) provide semantics to
expressions using the function
[[·]] : Expression -> (State -> State x Value)
.Value
is a subset ofExpression
.State
contains pairs(identifier,value). The semantic function takes an expression and delivers as
its meaning a function from the current state to the pair with the updated
state and a value. For example,
[[x]]
is the function from the current stateto the pair whose first component is the current state and whose second
component is the value of x. In contrast,
[[x++]]
is the function from thecurrent state to the pair whose first component is a state in which the value
of x is incremented, and whose second component is that very value. In this
sense, the context
[·]++
is referentially transparent iff it satisfies thedefinition given above.
I think functional programmers are entitled to use referential transparency in
the sense that they naturally recover
[[·]]
as a function from expressions to values.Functions are first-class values and the state can also be a value, not a
denotation. The state monad is (in part) a clean mechanism for passing (or
threading) the state.
请注意,“意义”的概念是发生在观察者头脑中的东西。 因此,相同的“参考”对于不同的人来说可能意味着不同的事情。 例如,我们在维基百科中有一个爱丁堡消歧页面。
编程环境中可能出现的一个相关问题可能是多态性。
也许我们应该为多态性(甚至可能是转换)的特殊情况命名,其中出于我们的目的,不同的多态性情况在语义上是等效的(而不是仅仅相似。例如,数字 1 - 可以表示为使用整数类型、复杂类型或任何其他类型——可以进行多态处理)。
Note that this concept of "meaning" is something that happens in the mind of the observer. Thus, the same "reference" can mean different things to different people. So, for example, we have an Edinburgh disambiguation page in Wikipedia.
A related issue which can show up in the context of programming might be polymorphism.
And perhaps we should have a name for the special case of polymorphism (or perhaps even casting) where for our purposes the differing polymorphic cases are semantically equivalent (as opposed to just being similar. For example, the number 1 -- which might be represented using an integer type, or a complex type or any of a variety of other types -- can be treated polymorphically).
引用透明度可以简单地表述为:
例如,编程语言Haskell是一种纯函数式语言; 这意味着它是引用透明的。
Referential transparency can be simply stated as:
For example, the programming language Haskell is a pure functional language; meaning that it is referentially transparent.
引用透明度是计算机科学中使用的术语。 它起源于数理逻辑,但在计算机科学中具有广泛的使用和有效的含义。
它的意思是:一个构造(例如函数)可以用其结果替换而不改变其含义。
在常用中,它是相似的,但不完全等同,到纯表达式。 纯表达式仅由其他纯表达式组成。 引用透明表达式可以在内部是不纯的,例如在计算过程中使用可变状态,但在整个表达式之外没有副作用。
所有纯函数,由于其构造,都是引用透明的,但反之则不一定。
许多语言功能支持不纯的引用透明性,例如 Haskell 中的 ST monad,以及 C++ 中的 constexpr 和某些 lambda。
有时会强制执行引用透明性,而有时程序员必须自己保证这一点。
Referential transparency is a term used in computer science. It originates from mathematical logic, but it has a widely used and therefore valid meaning in computer science.
It means: a construct (such as a function) that can be replaced by its result without changing its meaning.
In common use, it is similar, but not quite equivalent, to pure expressions. A pure expression is composed solely of other pure expressions. A referentially transparent expression can be internally impure, for example using mutable state in the process of its computation, but has no side effects outside of the expression as a whole.
All pure functions, by virtue of their construction, are referentially transparent, but not necessarily vice-versa.
Many language features support impure referential transparency, such as the
ST
monad in Haskell, andconstexpr
s and certain lambdas in C++.Sometimes referential transparency is enforced, and other times the programmer must guarantee it on their own.
编程中的引用透明性是指当向函数提供输入时,它将始终为所提供的给定输入返回相同的值。 以下面的示例函数为例。
对于给定的输入整数 x,该函数将始终返回相同的值。 上述函数的输出可以用其返回值替换,并且代码的操作应该相同。 例如,如果 x=10,则代码可以写为:
Int output = plusFive(10)
或
Int output = 15
Referential transparency in programming refers to when a function is supplied with an input it will always return the same value for the given input supplied. Taking the example function below.
The function will always return the same value for a given supplied input integer x. The output of the above function can be replaced by its returned value and the code should operate the same. For example if x=10 then the code can be written as either:
Int output = plusFive(10)
OR
Int output = 15
我知道我在这方面迟到了,但大多数帖子似乎对此都有误解。
引用透明度意味着您可以在给定 y=x(t) 的情况下将函数调用 x(t) 替换为 y。
你可以有$“输出是{y}”或$“输出是{x(t)}”
这意味着不能有副作用,因为如果状态改变,那么你不能用y替换x(t)。
然而,x(t) 仍然可以依赖于外部状态。 这就是引用透明函数与纯函数的区别。
I know I'm late to the party on this, but there seems to be misconceptions about this in most posts.
Referential Transparency means you can replace a function call x(t) with y given y=x(t).
You can have $"The output is {y}" or $"The output is {x(t)}"
This means there can be no side effects, because if state changes then you cannot replace x(t) with y.
However, x(t) can still be dependent on an external state. This is what separates Referentially Transparent Functions from Pure Functions.
“参照透明度”一词来自分析哲学,这是分析自然语言结构的哲学分支,基于逻辑和数学方法的陈述和论证。 换句话说,它是计算机科学之外最接近我们所说的编程语言语义的学科。 哲学家 Willard Quine 负责提出参照透明度的概念,但它也隐含在伯特兰·罗素和阿尔弗雷德·怀特海的方法。
从本质上讲,“引用透明度”是一个非常简单明了的想法。 术语“所指对象”在分析哲学中用于谈论表达式所指的事物。 它与我们在编程语言语义中所说的“含义”或“外延”大致相同。 使用 Andrew Birkett 的示例 (博客文章),“苏格兰首都”一词指的是爱丁堡市。 这是“参照物”的一个简单例子。
如果将上下文中的术语替换为指代同一实体的另一个术语不会改变含义,则句子中的上下文是“引用透明的”。 例如
意思是一样的
因此,上下文“苏格兰议会在……召开”是一个引用透明的上下文。 我们可以将“苏格兰的首都”替换为“爱丁堡”而不改变含义。 换句话说,上下文只关心该术语所指的内容,而不关心其他内容。 这就是上下文“参照透明”的含义。
另一方面,在句子中,
我们无法进行这样的替代。 如果我们这样做,我们会得到“Edinburgh has be Edinburgh since 1999”,这是一个疯狂的说法,并且传达的含义与原始句子不同。 因此,上下文“自 1999 年以来爱丁堡一直是……”似乎是引用不透明的(与引用透明相反)。 它显然比该术语所指的内容更关心一些事情。 它是什么?
诸如“苏格兰首都”之类的词被称为定语,长期以来,它们一直让逻辑学家和哲学家头疼不已。 罗素和蒯因对它们进行了整理,称它们实际上并不是“指称的”,即认为上述例子是用来指称实体的,这是错误的。 理解“爱丁堡自 1999 年以来一直是苏格兰首都”的正确方法是:
这句话不能转化为一句废话。 问题解决了! 奎因的观点是,自然语言是混乱的,或者至少是复杂的,因为它是为了方便实际使用而设计的,但哲学家和逻辑学家应该通过以正确的方式理解它们来带来清晰度。 引用透明度是一种用于带来这种意义清晰度的工具。
这一切与编程有什么关系? 事实上,并不多。 正如我们所说,参照透明度是一种用于理解语言的工具,即分配含义。 Christopher Strachey 是编程语言语义领域的创始人,他在意义研究中使用了它。 他的基础论文“编程语言的基本概念”可在网络上找到。 这是一篇漂亮的论文,每个人都可以阅读和理解它。 所以,请这样做。 你将会得到很大的启发。 他在本段中引入了“参照透明度”一词:
“本质上”的使用表明斯特雷奇正在解释它,以便用简单的术语解释它。 函数式程序员似乎以自己的方式理解这一段话。 论文中还出现了 9 次“引用透明度”,但他们似乎并不关心其他任何一个。 事实上,Strachey 的整篇论文都致力于解释命令式编程语言的含义。 但是,今天,函数式程序员声称命令式编程语言不是引用透明的。 斯特雷奇将会在坟墓里翻身。
我们可以挽救局势。 我们说自然语言是“混乱的,或者至少是复杂的”,因为它是为了方便实际使用而设计的。 编程语言也是同样的道理。 它们是“混乱的,或者至少是复杂的”,因为它们是为了方便实际使用而设计的。 这并不意味着他们需要让我们感到困惑。 只需使用引用透明的元语言以正确的方式理解它们,以便我们具有清晰的含义。 在我引用的论文中,斯特雷奇正是这么做的。 他通过将命令式编程语言分解为基本概念来解释它们的含义,从不丢失任何地方的清晰度。 他的分析的一个重要部分是指出编程语言中的表达式有两种“值”,称为左值和右值。 在斯特雷奇发表论文之前,这一点还没有被理解,而且混乱不堪。 如今,C 的定义经常提到它,并且每个 C 程序员都理解其中的区别。 (很难说其他语言的程序员是否同样能理解它。)
Quine 和 Strachey 都关心涉及某种形式的上下文依赖的语言结构的含义。 例如,我们的示例“爱丁堡自 1999 年以来一直是苏格兰的首都”表示“苏格兰首都”取决于考虑它的时间。 这种上下文依赖性在自然语言和编程语言中都是现实的。 即使在函数式编程中,自由变量和绑定变量也应根据它们出现的上下文进行解释。任何类型的上下文依赖都会以某种方式阻止引用透明度。 如果您试图理解术语的含义而不考虑它们所依赖的上下文,您将再次陷入混乱。 蒯因关心模态逻辑的含义。 他认为模态逻辑是指称不透明的,应该通过将其翻译为参考透明的框架(例如,将必要性视为可证明性)。 他基本上输掉了这场辩论。 逻辑学家和哲学家都认为克里普克的可能世界语义是完全足够的。 命令式编程也存在类似的情况。 Strachey 解释的状态依赖和 Reynolds 解释的存储依赖(以类似于 Kripke 的可能世界语义的方式)是完全足够的。 函数式程序员对这项研究了解不多。 他们关于引用透明性的想法需要持保留态度。
[附加说明:上面的例子表明,诸如“苏格兰首都”之类的简单短语具有多个层次的含义。 在某种程度上,我们现在可能正在谈论首都。 在另一个层面上,我们可能会谈论苏格兰在一段时间内可能拥有的所有可能的首都。 在正常实践中,我们可以很容易地“放大”特定上下文并“缩小”以跨越所有上下文。 自然语言的效率利用了我们这样做的能力。 命令式编程语言的高效性与此非常相似。 我们可以在赋值的右侧使用变量x(r-value)来讨论其在特定状态下的值。 或者,我们可能会谈论它跨越所有状态的l-value。 人们很少会对这些事情感到困惑。 然而,它们可能能够也可能不能精确地解释语言结构中固有的所有层次的含义。 所有这些层次的含义不一定是“显而易见的”,正确研究它们是一个科学问题。 然而,普通人对这种分层含义的解释不清晰并不意味着他们对此感到困惑。]
下面的一个单独的“后记”将这个讨论与函数式和命令式编程的关注点联系起来。。
The term "referential transparency" comes from analytical philosophy, the branch of philosophy that analyzes natural language constructs, statements and arguments based on the methods of logic and mathematics. In other words, it is the closest subject outside computer science to what we call programming language semantics. The philosopher Willard Quine was responsible for initiating the concept of referential transparency, but it was also implicit in the approaches of Bertrand Russell and Alfred Whitehead.
At its core, "referential transparency" is a very simple and clear idea. The term "referent" is used in analytical philosophy to talk about the thing that an expression refers to. It is roughly the same as what we mean by "meaning" or "denotation" in programming language semantics. Using Andrew Birkett's example (blog post), the term "the capital of Scotland" refers to the city of Edinburgh. That is a straightforward example of a "referent".
A context in a sentence is "referentially transparent" if replacing a term in that context by another term that refers to the same entity doesn't alter the meaning. For example
means the same as
So the context "The Scottish Parliament meets in ..." is a referentially transparent context. We can replace "the capital of Scotland" with "Edinburgh" without altering the meaning. To put another way, the context only cares about what the term refers to and nothing else. That is the sense in which the context is "referentially transparent."
On the other hand, in the sentence,
we can't do such a replacement. If we did, we would get "Edinburgh has been Edinburgh since 1999", which is a nutty thing to say, and doesn't convey the same meaning as the original sentence. So, it would seem that the context "Edinburgh has been ... since 1999" is referentially opaque (the opposite of referentially transparent). It apparently cares about something more than what the term refers to. What is it?
Things such as "the capital of Scotland" are called definite terms and they gave no lean amount of head ache to logicians and philosophers for a long time. Russell and Quine sorted them out saying that they are not actually "referential", i.e., it is a mistake to think that the above examples are used to refer to entities. The right way to understand "Edinburgh has been the capital of Scotland since 1999" is to say
This sentence cannot be transformed to a nutty one. Problem solved! The point of Quine was to say that natural language is messy, or at least complicated, because it is made to be convenient for practical use, but philosophers and logicians should bring clarity by understanding them in the right way. Referential transparency is a tool to be used for bringing such clarity of meaning.
What does all this have to do with programming? Not very much, actually. As we said, referential transparency is a tool to be used in understanding language, i.e., in assigning meaning. Christopher Strachey, who founded the field of programming language semantics, used it in his study of meaning. His foundational paper "Fundamental concepts in programming languages" is available on the web. It is a beautiful paper and everybody can read and understand it. So, please do so. You will be much enlightened. He introduces the term "referential transparency" in this paragraph:
The use of "in essence" suggests that Strachey is paraphrasing it in order to explain it in simple terms. Functional programmers seem to understand this paragraph in their own way. There are 9 other occurrences of "referential transparency" in the paper, but they don't seem to bother about any of the others. In fact, the whole paper of Strachey is devoted to explaining the meaning of imperative programming languages. But, today, functional programmers claim that imperative programming languages are not referentially transparent. Strachey would be turning in his grave.
We can salvage the situation. We said that natural language is "messy, or at least complicated" because it is made to be convenient for practical use. Programming languages are the same way. They are "messy, or at least complicated" because they are made to be convenient for practical use. That does not mean that they need to confuse us. They just have to be understood the right way, using a meta language that is referentially transparent so that we have clarity of meaning. In the paper I cited, Strachey does exactly that. He explains the meaning of imperative programming languages by breaking them down into elementary concepts, never losing clarity anywhere. An important part of his analysis is to point out that expressions in programming languages have two kinds of "values", called l-values and r-values. Before Strachey's paper, this was not understood and confusion reigned supreme. Today, the definition of C mentions it routinely and every C programmer understands the distinction. (Whether the programmers in other languages understand it equally well is hard to say.)
Both Quine and Strachey were concerned with the meaning of language constructions that involve some form of context-dependence. For example, our example "Edinburgh has been the capital of Scotland since 1999" signifies the fact that "capital of Scotland" depends on the time at which it is being considered. Such context-dependence is a reality, both in natural languages and programming languages. Even in functional programming, free and bound variables are to be interpreted with respect to the context in which they appear in. Context dependence of any kind blocks referential transparency in some way or the other. If you try to understand the meaning of terms without regard to the contexts they depend on, you would again end up with confusion. Quine was concerned with the meaning of modal logic. He held that modal logic was referentially opaque and it should be cleaned up by translating it into a referentially transparent framework (e.g., by regarding necessity as provability). He largely lost this debate. Logicians and philosophers alike found Kripke's possible world semantics to be perfectly adequate. Similar situation also reigns with imperative programming. State-dependence explained by Strachey and store-dependence explained by Reynolds (in a manner similar to Kripke's possible world semantics) are perfectly adequate. Functional programmers don't know much of this research. Their ideas on referential transparency are to be taken with a large grain of salt.
[Additional note: The examples above illustrate that a simple phrase such as "capital of Scotland" has multiple levels of meaning. At one level, we might be talking about the capital at the current time. At another level, we might talking about all possible capitals that Scotland might have had through the course of time. We can "zoom into" a particular context and "zoom out" to span all contexts quite easily in normal practice. The efficiency of natural language makes use of our ability to do so. Imperative programming languages are efficient in very much the same way. We can use a variable x on the right hand side of an assignment (the r-value) to talk about its value in a particular state. Or, we might talk about its l-value which spans all states. People are rarely confused by such things. However, they may or may not be able to precisely explain all the layers of meaning inherent in language constructs. All such layers of meaning are not necessarily 'obvious' and it is a matter of science to study them properly. However, the inarticulacy of ordinary people to explain such layered meanings doesn't imply that they are confused about them.]
A separate "postscript" below relates this discussion to the concerns of functional and imperative programming.
引用透明度是函数式编程中常用的术语,意味着给定一个函数和一个输入值,您将始终收到相同的输出。 也就是说函数中没有使用外部状态。
以下是引用透明函数的示例:
使用引用透明函数,给定输入和函数,您可以用值替换它,而不是调用函数。 因此,我们可以将其替换为 6,而不是使用参数 5 来调用 plusOne。
另一个很好的例子是一般数学。 在数学中,给定一个函数和一个输入值,它将始终映射到相同的输出值。 f(x) = x + 1。因此数学中的函数是引用透明的。
这个概念对研究人员来说很重要,因为它意味着当您拥有引用透明的函数时,它可以轻松实现自动并行化和缓存。
引用透明性总是在像 Haskell 这样的函数式语言中使用。
——
相反,有参照不透明的概念。 这意味着相反。 调用该函数可能并不总是产生相同的输出。
另一个例子是面向对象编程语言中的成员函数。 成员函数通常对其成员变量进行操作,因此是引用不透明的。 当然,成员函数可以是引用透明的。
另一个示例是从文本文件读取并打印输出的函数。 该外部文本文件可能随时更改,因此该函数将是引用不透明的。
Referential transparency, a term commonly used in functional programming, means that given a function and an input value, you will always receive the same output. That is to say there is no external state used in the function.
Here is an example of a referential transparent function:
With a referential transparent function, given an input and a function, you could replace it with a value instead of calling the function. So instead of calling plusOne with a parameter of 5, we could just replace that with 6.
Another good example is mathematics in general. In mathematics given a function and an input value, it will always map to the same output value. f(x) = x + 1. Therefore functions in mathematics are referentially transparent.
This concept is important to researchers because it means that when you have a referentially transparent function, it lends itself to easy automatic parallelization and caching.
Referential transparency is used always in functional languages like Haskell.
--
In contrast there is the concept of referential opaqueness. This means the opposite. Calling the function may not always produce the same output.
Another example, is a member function in an object oriented programming language. Member functions commonly operate on its member variables and therefore would be referential opaque. Member functions though can of course be referentially transparent.
Yet another example is a function that reads from a text file and prints the output. This external text file could change at any time so the function would be referentially opaque.
引用透明函数是一种仅依赖于其输入的函数。
A referentially transparent function is one which only depends on its input.
[这是我 3 月 25 日回答的后记,旨在让讨论更接近函数式/命令式编程的关注点。]
函数式程序员的引用透明性想法似乎在三个方面与标准概念不同:
函数式程序员似乎相信这些“值”存在于编程语言内部,而不是外部。 在这样做时,他们与哲学家和编程语言语义学家都不同。
他们似乎相信这些“值”应该是通过评估获得的。
例如,维基百科关于引用透明度的文章今天早上说:
这与哲学家/逻辑学家的说法完全不一致。 他们说,如果上下文中的表达式可以被引用相同事物的另一个表达式替换(核心引用表达式),则上下文是引用的或引用透明的。 这些哲学家/逻辑学家是谁? 它们包括Frege、罗素,怀特黑德,Carnap,Quine、Church 以及无数其他人。 他们每一个都是一个高耸的人物。 这些逻辑学家的综合智力至少可以说是惊天动地的。 他们一致认为所指/指称存在于形式语言之外,而语言中的表达只能谈论它们。 因此,人们在语言中所能做的就是用引用同一实体的另一个表达式替换一个表达式。 所指/指代本身不存在于语言中。 为什么函数式程序员会背离这个既定的传统呢?
人们可能会认为编程语言语义学家可能误导了他们。 但是,他们没有。
登陆:
故事:
鸟和瓦德勒:
因此,回想起来,兰丁和斯特雷奇通过用“价值”取代“所指”/“外延”来简化术语的努力可能是不明智的。 人们一听到“价值”,就会忍不住想到一个导致它的评估过程。 同样很容易将评估产生的任何内容视为“值”,即使很明显这不是外延。 这就是我认为在函数式程序员眼中“引用透明度”概念所发生的情况。 但是早期语义学家所说的“值”并不是评估的结果或函数的输出或任何类似的东西。 这是该术语的外延。
一旦我们将表达式的所谓“值”(古典哲学家话语中的“所指”或“外延”)理解为一个复杂的数学/概念对象,各种可能性就会出现。
函数式程序员不愿意称这种语言为“引用透明”,仅仅意味着他们不愿意承认这种复杂的数学/概念对象作为“值”。 另一方面,当状态转换器被放入他们自己喜欢的语法并用“monad”这样的流行词装饰时,他们似乎非常愿意将状态转换器称为“值”。 我不得不说,即使我们承认他们的“引用透明度”想法具有一定的一致性,他们也是完全不一致的。
回顾一下历史或许可以让我们了解这些混乱是如何产生的。 1962 年至 1967 年对于 Christopher Strachey 来说是非常紧张的时期。 1962 年至 65 年间,他在莫里斯·威尔克斯 (Maurice Wilkes) 那里兼职担任研究助理,设计并实现了后来被称为 CPL 的编程语言。 这是一种命令式编程语言,但也具有强大的函数式编程语言功能。 Landin 是 Strachey 咨询公司的员工,他对 Strachey 对编程语言的看法产生了巨大影响。 在 1965 年具有里程碑意义的论文“Next 700 种编程语言”中,Landin 毫不掩饰地推广函数式编程语言(称它们是外延语言),并将命令式编程语言描述为它们的“对立面”。 在随后的讨论中,我们发现斯特雷奇对兰丁的强势地位提出了质疑。
1965 年,斯特雷奇担任牛津大学讲师,似乎基本上全职致力于发展命令式和跳跃理论。 到 1967 年,他已经准备好一种理论,并在“Martin Campbell-Kelly< /a>)
获取斯特雷奇著作的困难可能导致人们依赖二手资料和道听途说而产生混乱,但是,现在“基本概念"在网上很容易找到,没有必要依靠猜测,我们应该阅读它并自己编写。请注意 Strachey 的含义:
要么通过将表达式分解为多个命令来消除,并且更简单
表达式,或者,如果这变得困难,则成为注释的主题。”
如果不理解 L 值、R 值和填充命令式程序员概念宇宙的其他复杂对象之间的区别,任何关于“引用透明度”的讨论都是从根本上来说是错误的。
[This is a postscript to my answer from March 25, in an effort to bring the discussion closer to the concerns of functional/imperative programming.]
The functional programmers' idea of referential transparency seems to differ from the standard notion in three ways:
Whereas the philosophers/logicians use terms like "referent", "denotation", "designatum" and "bedeutung" (Frege's German term), functional programmers use the term "value". (This is not entirely their doing. I notice that Landin, Strachey and their descendants also used the term "value" to talk about referent/denotation. It may be just a terminological simplification that Landin and Strachey introduced, but it seems to make a big difference when used in a naive way.)
Functional programmers seem to believe that these "values" exist within the programming language, not outside. In doing this, they differ from both the philosophers and the programming language semanticists.
They seem to believe that these "values" are supposed to be obtained by evaluation.
For example, the Wikipedia article on referential transparency says, this morning:
This is completely at variance with what the philosophers/logicians say. They say that a context is referential or referentially transparent if an expression in that context can be replaced by another expression that refers to the same thing (a coreferential expression). Who are these philosophers/logicians? They include Frege, Russell, Whitehead, Carnap, Quine, Church and countless others. Each one of them is a towering figure. The combined intellectual power of these logicians is earth-shattering to say the least. All of them are unanimous in the position that referents/denotations exist outside the formal language and expressions within the language can only talk about them. So, all that one can do within the language is to replace one expression by another expression that refers to the same entity. The referents/denotations themselves do not exist within the language. Why do the functional programmers deviate from this well-established tradition?
One might presume that the programming language semanticists might have misled them. But, they didn't.
Landin:
Stoy:
Bird and Wadler:
So, in retrospect, the efforts of Landin and Strachey to simplify the terminology by replacing "referent"/"denotation" with "value" might have been injudicious. As soon as one hears of a "value", there is a temptation to think of an evaluation process that leads to it. It is equally tempting to think of whatever the evaluation produces as the "value", even though it might be quite clear that that is not the denotation. That is what I gather to have happened to the concept of "referential transparency" in the eyes of functional programmers. But the "value" that was being spoken of by the early semanticists is not the result of an evaluation or the output of a function or any such thing. It is the denotation of the term.
Once we understand the so-called "value" of an expression ("referent" or "denotation" in classical philosophers' discourse) as a complex mathematical/conceptual object, all kinds of possibilities open up.
The reluctance of functional programmers to call such languages "referentially transparent" merely implies that they are reluctant to admit such complex mathematical/conceptual objects as "values". On the other hand, they seem perfectly willing to call a state transformer a "value" when it is put in their own favourite syntax and dressed up with a buzz word like "monad". I have to say that they are being entirely inconsistent, even if we grant it to them that their idea of "referential transparency" has some coherence.
A bit of history might throw some light on how these confusions came into being. The period between 1962 to 1967 was a very intensive one for Christopher Strachey. Between 1962-65, he took a part-time job as a research assistant with Maurice Wilkes to design and implement the programming language that came to be known as CPL. This was an imperative programming language but was meant to have powerful functional programming language capabilities as well. Landin, who was an employee of Strachey in his consultancy company, had a huge influence on Strachey's view of programming languages. In the landmark 1965 paper "Next 700 programming languages", Landin unabashedly promotes functional programming languages (calling them denotative languages) and describes imperative programming languages as their "antithesis". In the ensuing discussion, we find Strachey raising doubts on Landin's strong position.
In 1965, Strachey took the position of a Reader at Oxford and seems to have worked essentially full-time on developing a theory of imperatives and jumps. By 1967, he was ready with a theory, which he taught in his course on "Fundamental concepts in programming languages" in a Copenhagen summer school. The lecture notes were supposed to have been published but "unfortunately, because of dilatory
editing, the proceedings never materialized; like
much of Strachey’s work at Oxford, however, the
paper had an influential private circulation." (Martin Campbell-Kelly)
The difficulty of obtaining Strachey's writings could have led to the confusions being propagated, with people relying on secondary sources and hearsay. But, now that "Fundamental concepts" is readily available on the web, there is no need to resort to guess work. We should read it and make up our own mind as to what Strachey meant. In particular:
either be eliminated by decomposing the expression into several commands and simpler
expressions, or, if this turns out to be difficult, the subject of a comment."
Any talk of "referential transparency" without understanding the distinction between L-values, R-values and other complex objects that populate the imperative programmer's conceptual universe is fundamentally mistaken.
如果表达式可以用其值替换,而不更改算法,从而产生对相同输入具有相同效果和输出的算法,则该表达式是引用透明的。
An expression is referentially transparent if it can be replaced with its value, without changing the algorithm, yielding an algorithm that has the same effects and output on the same input.
引用透明函数是一种行为类似于数学函数的函数; 给定相同的输入,它将始终产生相同的输出。 这意味着传入的状态未被修改,并且该函数没有自己的状态。
A referentially transparent function is one which acts like a mathematical function; given the same inputs, it will always produce the same outputs. It implies that the state passed in is not modified, and that the function has no state of its own.
对于那些需要简明解释的人,我将冒险进行解释(但请阅读下面的披露)。
编程语言中的引用透明度促进了等式推理——引用透明度越高,进行等式推理就越容易。 例如,使用(伪)函数定义,
fx = x + x,
您可以轻松(安全地)在此定义范围内用 foo + foo 替换 f(foo),而无需对执行位置有太多限制这种减少很好地表明了您的编程语言具有多少引用透明度。
例如,如果 foo 在 C 编程意义上是 x++,那么您就无法安全地执行此缩减(也就是说,如果您要执行此缩减,您最终不会得到与开始时相同的程序)。
在实际的编程语言中,你不会看到完美的引用透明度,但函数式程序员比大多数人更关心它(参见 Haskell,它是一个核心目标)。
(全面披露:我是一名函数式程序员,因此根据最佳答案,您应该对这个解释持保留态度。)
For those in need of a concise explanation I will hazard one (but read the disclosure below).
Referential transparency in a programming language promotes equational reasoning -- the more referential transparency you have the easier it is to do equational reasoning. E.g. with a (pseudo) function definition,
f x = x + x,
the ease with which you can (safely) replace f(foo) with foo + foo in the scope of this definition, without having too many constraints on where you can perform this reduction, is a good indication of how much referential transparency your programming language has.
For example if foo were x++ in the C programming sense then you could not perform this reduction safely (which is to say, if you were to perform this reduction you would't end up with the same program that you started with).
In practical programming languages you won't see perfect referential transparency but functional programmers care about it more than most (cf Haskell, where it is a core objective).
(Full disclosure: I am a functional programmer so by the top answer you should take this explanation with a grain of salt.)
如果您对词源感兴趣(即为什么这个概念有这个特定的名称),请查看我的 关于该主题的博客文章。 该术语来自哲学家/逻辑学家奎因。
If you're interested in the etymology (ie. why does this concept have this particular name), have a look at my blog post on the topic. The terminology comes from the philosopher/logician Quine.
在 1 中,所讨论的两种语言是明确的:
在 2 中,由于对象和元语言的接近性,它们可能会被混淆。
作为一名语言实现者,我发现我需要不断记住这种区别。
雷迪教授,我可以这样解释你吗:-)
In 1 there is a clarity of two languages in question:
In 2, thanks to the closeness of the object and metalanguages, they can be confused.
As a language implementer, I find that I need to constantly remember this distinction.
So Prof. Reddy may I paraphrase you thus :-)