简体   繁体   English

了解参考透明度

[英]understanding referential transparency

Generally, I have a headache because something is wrong with my reasoning: 一般来说,我很头疼因为我的推理出了问题:

  1. For 1 set of arguments, referential transparent function will always return 1 set of output values. 对于1组参数,参照透明函数将始终返回1组输出值。

  2. that means that such function could be represented as a truth table (a table where 1 set of output parameters is specified for 1 set of arguments). 这意味着这样的函数可以表示为真值表(一组表为1组参数指定了一组输出参数)。

  3. that makes the logic behind such functions is combinational (as opposed to sequential) 这使得这些功能背后的逻辑组合的(而不是顺序的)

  4. that means that with pure functional language (that has only rt functions) it is possible to describe only combinational logic. 这意味着使用纯函数语言(只有rt函数),可以只描述组合逻辑。

The last statement is derived from this reasoning, but it's obviously false; 最后一个陈述来自这个推理, 它显然是错误的; that means there is an error in reasoning. 这意味着推理有误。 [question: where is error in this reasoning?] [问题:这个推理在哪里出错?]

UPD2. UPD2。 You, guys, are saying lots of interesting stuff, but not answering my question. 你们这些人说的很多有趣的东西,但没有回答我的问题。 I defined it more explicitly now. 我现在更明确地定义它。 Sorry for messing up with question definition! 对不起弄乱问题定义!

Question: where is error in this reasoning? 问题:这个推理在哪里出错?

A referentially transparent function might require an infinite truth table to represent its behavior. 引用透明函数可能需要无限真值表来表示其行为。 You will be hard pressed to design an infinite circuit in combinatory logic. 您将很难在组合逻辑中设计无限电路。

Another error: the behavior of sequential logic can be represented purely functionally as a function from states to states. 另一个错误:顺序逻辑的行为可以纯粹在功能上表示为从状态到状态的函数。 The fact that in the implementation these states occur sequentially in time does not prevent one from defining a purely referentially transparent function which describes how state evolves over time. 在实现中这些状态在时间上顺序发生的事实并不妨碍人们定义纯粹的引用透明函数,该函数描述状态如何随时间演变。

Edit: Although I apparently missed the bullseye on the actual question, I think my answer is pretty good, so I'm keeping it :-) (see below). 编辑:虽然我在实际问题上显然错过了靶心,但我认为我的答案非常好,所以我保留它:-)(见下文)。

I guess a more concise way to phrase the question might be: can a purely functional language compute anything an imperative one can? 我想用一种更简洁的方式来表达这个问题可能是:一个纯函数式的语言可以计算出任何必要的东西吗?

First of all, suppose you took an imperative language like C and made it so you can't alter variables after defining them. 首先,假设您使用了像C这样的命令式语言,并且在定义它们之后无法更改变量。 Eg: 例如:

int i;

for (i = 0;  // okay, that's one assignment
     i < 10; // just looking, that's all
     i++)    // BUZZZ!  Sorry, can't do that!

Well, there goes your for loop. 好吧,那就是你for循环。 Do we get to keep our while loop? 我们能保持我们的while循环吗?

while (i < 10)

Sure, but it's not very useful. 当然,但它不是很有用。 i can't change, so it's either going to run forever or not run at all. i无法改变,所以它要么永远运行,要么根本不运行。

How about recursion? 递归怎么样? Yes, you get to keep recursion, and it's still plenty useful: 是的,你可以保持递归,它仍然很有用:

int sum(int *items, unsigned int count)
{
    if (count) {
        // count the first item and sum the rest
        return *items + sum(items + 1, count - 1);
    } else {
        // no items
        return 0;
    }
}

Now, with functions, we don't alter state, but variables can, well, vary. 现在,通过函数,我们不会改变状态,但变量可以变化。 Once a variable passes into our function, it's locked in. However, we can call the function again (recursion), and it's like getting a brand new set of variables (the old ones stay the same). 一旦变量进入我们的函数,它就会被锁定。但是,我们可以再次调用函数(递归),这就像获得一组全新的变量(旧的变量保持不变)。 Although there are multiple instances of items and count , sum((int[]){1,2,3}, 3) will always evaluate to 6 , so you can replace that expression with 6 if you like. 虽然有多个itemscount实例,但sum((int[]){1,2,3}, 3)总是会计算为6 ,因此如果您愿意,可以将该表达式替换为6

Can we still do anything we want? 我们还能做任何我们想做的事吗? I'm not 100% sure, but I think the answer is "yes". 我不是百分百肯定,但我认为答案是肯定的。 You certainly can if you have closures, though. 不过,如果你有封闭,你当然可以。


You have it right. 你没事。 The idea is, once a variable is defined, it can't be redefined. 这个想法是,一旦定义了变量,就不能重新定义它。 A referentially transparent expression, given the same variables, always yields the same result value. 给定相同变量的引用透明表达式总是产生相同的结果值。

I recommend looking into Haskell, a purely functional language. 我建议研究Haskell,一种纯函数式语言。 Haskell doesn't have an "assignment" operator, strictly speaking. 严格来说,Haskell没有“赋值”运算符。 For instance: 例如:

my_sum numbers = ??? where
    i     = 0
    total = 0

Here, you can't write a "for loop" that increments i and total as it goes along. 在这里,你不能写一个“for循环”,它增加i和总和。 All is not lost, though. 不过,一切都不会丢失。 Just use recursion to keep getting new i s and total s: 只需使用递归来获取新的itotal s:

my_sum numbers = f 0 0 where
    f i total =
        if i < length numbers
            then f i' total'
            else total
        where
            i' = i+1
            total' = total + (numbers !! i)

(Note that this is a stupid way to sum a list in Haskell, but it demonstrates a method of coping with single assignment.) (请注意,这是在Haskell中对列表求和的一种愚蠢方法,但它演示了一种处理单个赋值的方法。)

Now, consider this highly imperative-looking code: 现在,考虑一下这个看起来非常重要的代码:

main = do
    a <- readLn
    b <- readLn
    print (a + b)

It's actually syntactic sugar for: 它实际上是语法糖:

main =
    readLn >>= (\a ->
    readLn >>= (\b ->
    print (a + b)))

The idea is, instead of main being a function consisting of a list of statements, main is an IO action that Haskell executes, and actions are defined and chained together with bind operations. 这个想法是,main不是由一个语句列表组成的函数,而是一个Haskell执行的IO动作,并且动作是通过绑定操作定义和链接在一起的。 Also, an action that does nothing, yielding an arbitrary value, can be defined with the return function. 此外,可以使用return函数定义不执行任何操作,产生任意值的操作。

Note that bind and return aren't specific to actions. 请注意,绑定和返回并非特定于操作。 They can be used with any type that calls itself a Monad to do all sorts of funky things. 它们可以用于任何称自己为Monad的类型来做各种各样的时髦事物。

To clarify, consider readLn . 为了澄清,请考虑readLn readLn is an action that, if executed, would read a line from standard input and yield its parsed value. readLn是一个动作,如果执行,将从标准输入读取一行并产生其解析的值。 To do something with that value, we can't store it in a variable because that would violate referential transparency : 要使用该值执行某些操作,我们无法将其存储在变量中,因为这会违反参照透明度

a = readLn

If this were allowed, a's value would depend on the world and would be different every time we called readLn , meaning readLn wouldn't be referentially transparent. 如果允许这样做,则a的值将取决于世界,并且每次调用readLn时都会有所不同,这意味着readLn不会在引用上透明。

Instead, we bind the readLn action to a function that deals with the action, yielding a new action, like so: 相反,我们将readLn操作绑定到一个处理操作的函数,产生一个新的操作,如下所示:

readLn >>= (\x -> print (x + 1))

The result of this expression is an action value. 该表达式的结果是一个动作值。 If Haskell got off the couch and performed this action, it would read an integer, increment it, and print it. 如果Haskell离开沙发并执行此操作,它将读取整数,递增并打印它。 By binding the result of an action to a function that does something with the result, we get to keep referential transparency while playing around in the world of state. 通过将动作的结果绑定到对结果执行某些操作的函数,我们可以在状态世界中进行游戏时保持参照透明度。

As far as I understand it, referential transparency just means: A given function will always yield the same result when invoked with the same arguments. 据我所知,引用透明性只是意味着:当使用相同的参数调用时,给定的函数将始终产生相同的结果。 So, the mathematical functions you learned about in school are referentially transparent. 因此,您在学校学到的数学函数是参考透明的。

A language you could check out in order to learn how things are done in a purely functional language would be Haskell . Haskell是一种你可以查看的语言,用于学习如何用纯函数语言完成任务。 There are ways to use "updateable storage possibilities" like the Reader Monad, and the State Monad for example. 有一些方法可以使用“可更新的存储可能性”,例如Reader Monad和State Monad If you're interested in purely functional data structures, Okasaki might be a good read. 如果您对纯功能数据结构感兴趣, Okasaki可能是一个很好的阅读。

And yes, you're right: Order of evaluation in a purely functional language like haskell does not matter as in non-functional languages, because if there are no side effects, there is no reason to do someting before/after something else -- unless the input of one depends on the output of the other, or means like monads come into play. 是的,你是对的:像非功能语言这样的纯功能语言中的评估顺序无关紧要,因为如果没有副作用,就没有理由在其他东西之前/之后做一些事情 - 除非一个的输入取决于另一个的输出,或者像monad的方式起作用。

I don't really know about the truth-table question. 我真的不知道真相表问题。

Here's my stab at answering the question: 这是我回答这个问题的准备:

Any system can be described as a combinatorial function, large or small. 任何系统都可以描述为组合函数,无论大小。

There's nothing wrong with the reasoning that pure functions can only deal with combinatorial logic -- it's true, just that functional languages hide that from you to some extent or another. 纯函数只能处理组合逻辑的原因并没有错 - 这是正确的,只是函数式语言在某种程度上隐藏了你。

You could even describe, say, the workings of a game engine as a truth table or a combinatorial function. 你甚至可以将游戏引擎的工作原理描述为真值表或组合函数。

You might have a deterministic function that takes in "the current state of the entire game" as the RAM occupied by the game engine and the keyboard input, and returns "the state of the game one frame later". 您可能有一个确定性函数,它将“整个游戏的当前状态”作为游戏引擎占用的RAM和键盘输入,并返回“一帧之后的游戏状态”。 The return value would be determined by the combinations of the bits in the input. 返回值将由输入中的位组合确定。

Of course, in any meaningful and sane function, the input is parsed down to blocks of integers, decimals and booleans, but the combinations of the bits in those values is still determining the output of your function. 当然,在任何有意义且理智的函数中,输入被解析为整数,小数和布尔值的块,但这些值中的位组合仍然确定函数的输出。

Keep in mind also that basic digital logic can be described in truth tables. 还要记住,基本数字逻辑可以在真值表中描述。 The only reason that that's not done for anything more than, say, arithmetic on 4-bit integers, is because the size of the truth table grows exponentially. 除了4位整数的算术之外,没有做到这一点的唯一原因是因为真值表的大小呈指数级增长。

The error in Your reasoning is the following: 您的推理中的错误如下:
"that means that such function could be represented as a truth table". “这意味着这种功能可以表示为真值表”。

You conclude that from a functional language's property of referential transparency. 您可以从功能语言的参照透明度属性中得出结论。 So far the conclusion would sound plausible, but You oversee that a function is able to accept collections as input and process them in contrast to the fixed inputs of a logic gate. 到目前为止,结论听起来似乎有道理,但您监督一个函数能够接受集合作为输入并与逻辑门的固定输入相比较处理它们。

Therefore a function does not equal a logic gate but rather a construction plan of such a logic gate depending on the actual (at runtime determined) input! 因此,功能不等于逻辑门,而是取决于实际(在运行时确定的)输入时这种逻辑门的构造方案!

To comment on Your comment: Functional languages can - although stateless - implement a state machine by constructing the states from scratch each time they are being accessed. 评论您的评论:功能语言可以 - 尽管是无状态的 - 通过每次访问时从头开始构建状态来实现状态机。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM