Date.now是否参照透明?

DateTime.Now或Date.now是否参照透明?

这是Qiita函数式编程文章中有争议的话题之一。

首先,我们必须非常小心,因为“参考透明”这个词在某种意义上是棘手的词/概念,并且存在一个突出的讨论

什么是参照透明度?

提问者指出:

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

一个非常典型的解释,但通常导致我们误解的观点如下:(#Brian R. Bondy在上面第二页的答案)

引用透明性是函数式编程中常用的一个术语,意味着给定一个函数和一个输入值,您将始终收到相同的输出。 这就是说在函数中没有使用外部状态。

我总是听到和认为错误的典型声明是这样的:

在编程语言中, Date.now 总是返回一个与当前时间相对应的不同值 ,并按照

给定一个函数和一个输入值,你总会收到相同的输出。

因此, Date.now不是参照透明的!

我知道一些(功能性)程序员坚信上面的说法是值得信赖的,然而,@ Uday Reddy回答#1和#3的解释如下:

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

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

  • 鉴于哲学家/逻辑学家使用诸如“参考”,“外延”,“指定”和“理论”(弗雷格的德语术语)等术语,功能性程序员使用术语“价值”。 (这不完全是他们的行为,我注意到Landin,Strachey和他们的后代也用“价值”这个词来谈论参考/表达,这可能只是Landin和Strachey引入的术语简化,但它似乎使以天真的方式使用时有很大的区别。)

  • 功能程序员似乎认为这些“价值”存在于编程语言中,而不是外部。 在这样做的时候,他们不同于哲学家和编程语言语义学家。

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

  • 来想一想,“外部状态”也是棘手的词/概念。

    引用透明性是函数式编程中常用的一个术语,意味着给定一个函数和一个输入值,您将始终收到相同的输出。 这就是说在函数中没有使用外部状态。

    是“当前时间”“外部状态”还是“外部价值”?

    如果我们把“当前时间”称为“外部状态”,那么“鼠标事件”怎么样?

    “鼠标事件”不是应该由编程上下文管理的状态,而是一个外部事件

    给定一个函数和一个输入值,你总会收到相同的输出。

    所以我们可以理解如下:

    “当前时间”既不是“输入值”,也不是“外部值”,也不是“外部状态”,并且Date.now始终返回与正在进行的事件“当前时间”相对应的相同输出。

    如果仍然坚持或想要将“当前时间”称为“价值” ,再次,

  • 功能程序员似乎认为这些“价值”存在于编程语言中,而不是外部。 在这样做的时候,他们不同于哲学家和编程语言语义学家。
  • “当前时间”的值从来不存在于编程语言中,但仅存在于外部 ,而外部的“当前时间”值通过不是编程上下文而是现实世界的时间流程明显更新

    因此,我明白Date.now是参照透明的。

    我想读你的想法。 谢谢。


    EDIT1

    什么是(功能性)反应式编程?

    Conal Elliott @Conal也解释了功能反应式编程(FRP)。

    他是最早开发玻璃钢的人之一,他这样解释道:

  • FRP是关于“随着时间的推移表示价值的数据类型”

  • 动态/不断变化的值(即“随着时间推移”的值)本身就是第一类值。

  • 在这个FRP的角度来看,

  • Date可以被看作是“时间轴上”不可变对象的“一段时间内”的第一类值。

  • .now是在Date内解决“当前时间”的属性/函数

    因此Date.time返回表示我们“当前时间”的不可变和参照透明值。


  • EDIT2

    (在JavaScript中)

    指称透明功能

    let a = 1;
    
    let f = () => (a);
    

    Function:f输入Function:f是none; Function:f输出Function:f取决于a依赖于f之外的上下文的f

    引用透明函数

    let t = Date.now();
    
    let f = (Date) => (Date.now());
    

    虽然Date值存在于我们的物理世界中,但Date可以被看作是“不断变化的”FRP一等价值。

    由于从任何编程上下文引用的Date是相同的,我们通常隐含地可以省略Date作为输入值并且简单地如此

    let f = () => (Date.now());
    

    EDIT3

    实际上,我通过电子邮件发送给Conal Elliott @Conal,他是FRP最早的开发人员之一。 他很好地回复并告诉我这里有一个类似的问题。

    函数式编程中如何存在时间函数?

    提问者指出:

    所以我的问题是:函数式编程中是否可以使用时间函数(返回当前时间)?

  • 如果是,那么它如何存在? 它不违反函数式编程的原则吗? 它特别违反了参照透明性,这是函数式编程的一个属性(如果我正确理解它的话)。

  • 或者如果不是,那么在函数式编程中如何知道当前的时间?

  • 以及Conal Elliott @Conal在答案中的回答:

    是的,如果将该时间作为参数给定,则纯函数可能会返回时间。 不同的时间参数,不同的时间结果。 然后形成时间的其他功能,并将它们与简单的函数(时间)转换(高阶)函数词汇结合起来。 由于该方法是无状态的,因此这里的时间可以是连续的(与分辨率无关)而不是离散的,这大大提高了模块性。 这种直觉是功能反应规划(FRP)的基础。


    Edit4我对@Roman Sausarnes的回答表示赞赏。

    请允许我介绍我对功能编程和FRP的看法。

    首先,我认为编程基本上都是关于数学,功能编程追求这方面。 另一方面,命令式编程是描述机器操作步骤的一种方式,不一定是数学。

    像Haskel这样的纯函数式编程在处理“状态”或IO方面有一定难度,我认为整个问题都来自“时间”。

    “状态”或“时间”对我们来说是非常主观的实体,人类。 我们自然而然地认为“时间”是流动或传递,“状态”在变化,这是天真的现实主义。

    我认为Naïve对“时间”的现实主义是编程界所有混淆的基本危险和原因,很少有人讨论这个问题。 在现代物理学,甚至牛顿物理学中,我们都用纯粹的数学方法来对待时间,所以如果我们用物理学的方式来概述我们的世界,那么用纯粹的数学函数式编程来对待我们的世界并不困难。

    所以,我概述了我们的世界/宇宙是不可变的,像预先录制的DVD一样,只有我们的主观观点是可变的,包括“时间”或“状态”。

    在编程中,不变的宇宙和我们可变的主观经验之间唯一的联系就是“事件”。 纯粹的函数式编程语言,如Haskell,基本上缺乏这种观点,尽管包括Cornel Elliott在内的一些富有洞察力的研究人员进行FRP,但大多数人仍然认为FRP方法仍然很小或很难使用,并且其中许多人当然会视为可变状态。

    当然,FRP是唯一的智能解决方案,尤其是作为创始人的Cornel Elliott应用了这一哲学视角,并宣布“随着时间推移”的一流价值 。 也许不幸的是,很多程序员不明白他的真正含义,因为他们被朴素的现实主义所困,并且很难将“时间”看作是哲学上的,或者是身体上不可变的实体。

    因此,如果他们为了数学完整性/一致性的优势讨论“ 纯函数 ”或“ 参考透明 ”,那么对于我来说,“Date.now”在纯函数式编程中自然是引用透明的,因为“Date.time”访问某个永恒不变的宇宙不可改变的时间点。


    那么在@Reddy或@Roman Sausarnes disucusses这样的指称语义中引用透明是什么呢?

    我概述FP中的参照透明性,尤其是在Haskell社区中,关于数学完整性/一致性。

    当然,也许我可以按照Haskell社区提供的“引用透明度”的更新定义,并且实际上,如果我们判断它不是引用透明,正确的话,我们判断代码在数学上是不一致的?

    其实,再次,

    函数式编程中如何存在时间函数?

    一名程序员质疑如下:

    所以我的问题是:函数式编程中是否可以使用时间函数(返回当前时间)?

  • 如果是,那么它如何存在? 它不违反函数式编程的原则吗? 它特别违反了参照透明性,这是函数式编程的一个属性(如果我正确理解它的话)。

  • 或者如果不是,那么在函数式编程中如何知道当前的时间?


  • 共识

    违反函数式编程的原则

    =违反参照透明性,这是函数式编程的一个属性

    =数学不一致!


    这是我们常见的看法,对吗?

    在这个问题中,许多人回答说,“返回当前时间的函数”在Haskell社区的“参照透明度”的定义中不是特别透明的,许多人提到它是关于数学一致性的。

    然而,只有少数回答“返回当前时间的函数”是参照透明的,其中一个答案来自Conal Elliott @Conal的FRP视角。

    国际海事组织(IMO),玻璃钢公司(FRP)将处理时间流的观点视为“随着时间的推移而成为一流的不变价值”,正如我上面提到的那样,像数学原理一样是一种正确的方式。

    那么如何将“Date.now”/“函数返回当前时间”变成Haskell上下文的参考透明?

    那么,我能想到的唯一解释是Haskell社区更新的“参照透明度”定义有些不对。

    事件驱动和数学完整性/一致性

    我提到 - 在编程中,不可变的宇宙和我们可变的主观经验之间的唯一联系是“事件”或“事件驱动”。

    功能编程以事件驱动的方式进行评估,另一方面,通过代码中描述的机器操作的步骤/例程评估命令式编程。

    “Date.now”取决于“事件”,并且原则上,“事件”对于代码的上下文是未知的。

    那么,事件驱动是否会破坏数学的完整性/一致性? 绝对不。

    将语法映射到含义 - 索引(食指)

    CS皮尔斯介绍了术语'索引'来表明指向的想法(如'食指')。 ⟦I⟧,[[here]],[[now]]等。

    可能这是Haskell中“Monad”,“functor”事物的数学上相同的概念。 在Haskell的指称语义中,[[now]]作为'食指'是清楚的。

    索引(食指)是主观的,事件驱动也是

    [[I]],[[here]],[[now]]等等是主观的,而且在编程中,不可变客观宇宙和我们可变主观经验之间的唯一联系是“事件”或“事件驱动的”

    因此,只要将[[now]]绑定到“事件驱动”编程的事件声明,我认为主观(依赖于上下文)的数学不一致从不会发生。


    Edit5

    @Bergi给了我一个很好的评论:

    是的, Date.now ,外部值,是引用透明的。 它总是意味着“当前时间”。

    Date.now()不是,这是一个函数调用,根据外部状态返回不同的数字。 当前时间的概念透明的问题是我们无法用它计算任何东西。

    @KenOKABE:似乎与Date.now()相同。 问题在于,它并不意味着同一时间的当前时间,而是在不同的时间 - 一个程序需要时间来执行,这就是它不纯的原因。

    当然,我们可以设计一个引用透明的Date.now函数/ getter,它总是返回程序开始的时间(就像程序执行是立即的),但这不是Date.now()/Date.Now工作方式。 它们取决于程序的执行状态。 - Bergi

    我认为我们需要讨论它。

    Date.now ,外部值,是引用透明的。

    [[Date.now]]就像我在#Edit4中提到的那样,它是一种主观的索引手指,但只要它仍然处于索引域(没有执行/评估),它就是透明的 ,我们同意上。

    然而,@Bergi建议Date.now() (带有执行/评估)在不同的时间返回“不同的值”,而不再是透明的。 我们还没有达成一致。

    我认为他肯定已经表明了这个问题,但只存在于命令式编程中:

    console.log(Date.now()); //some numeric for 2016/05/18 xx:xx:xx ....
    console.log(Date.now()); //different numeric for 2016/05/18 xx:xx:xx ....
    

    在这种情况下, Date.now()不是透明的,我同意。

    但是,在函数式编程/声明式编程范例中,我们永远不会像上面那样写出来。 我们必须这样写:

    const f = () => (Date.now());
    

    并且,这个f在一些“事件驱动”的环境中被评估 。 这就是函数式编程代码的行为。

    是的, 这段代码是一样的

    const f = Date.now;
    

    因此,在函数式编程/声明式编程范例中, Date.nowDate.now() (带有执行/评估)绝不会在不同时间返回“不同值”的问题。

    因此,正如我在EDIT4中所提到的,只要[现在]]绑定到“事件驱动”编程的事件声明,我认为主观(依赖于上下文)的数学不一致从不会发生。


    好吧,我要刺穿这个。 我不是这方面的专家,但我花了一些时间思考@ UdayReddy对你连接的这个问题的答案,我想我已经把我的脑袋缠住了。

    分析哲学中的参照透明性

    我认为你必须从雷迪先生回答其他问题的地方开始。 雷迪先生写道:

    术语“指称对象”在分析哲学中用于谈论表达所指的事物。 它与我们在编程语言语义中的“意义”或“外延”意思大致相同。

    注意使用“外延”这个词。 编程语言具有语法或语法,但它们也具有语义或含义。 指称语义学是将语言的语法转化为数学意义的实践。

    据我所知,指称语义虽然是理解,设计和推理计算机程序最有力的工具之一,但它并不被广泛理解。 我需要花一点时间来为你的问题的答案奠定基础。

    指称语义:将语法映射到含义

    指称语义背后的思想是,计算机语言中的每个语法元素都有相应的数学意义或语义。 指称语义是语法和语义之间的显式映射。 采取句法数字1 。 你可以将它映射到它的数学意义上,这只是数学数字1 。 语义功能可能如下所示:

    syntax
       ↓
      ⟦1⟧ ∷ One
            ↑ 
        semantics
    

    有时候,双方括号用于表示“含义”,在这种情况下,语义方面的数字1拼写为“ One 。 这些只是用于指示我们何时在讨论语义以及何时讨论语法的工具。 您可以阅读功能是指,“句法符号的含义1是数字One 。”

    我在上面使用的例子看起来微不足道。 当然, 1意味着One 。 还有什么意思呢? 但是,它不一定要。 你可以这样做:

    ⟦1⟧ ∷ Four
    

    那将是愚蠢的,没有人会使用这种愚蠢的语言,但它将是一个有效的语言。 但重要的是,指称语义允许我们明确我们编写的程序的数学意义。 下面是一个用lambda表示法对整数x进行平方的函数的表达式:

    ⟦square x⟧ ∷ λx → x²
    

    现在我们可以继续讨论参考透明度。

    参照透明度是关于意义

    让我再次搭上乌代先生的回答。 他写:

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

    将这个问题与你向平均程序员询问参考透明度意味着什么时得到的答案相比较。 他们通常会说出类似上面引用的答案:

    引用透明性是函数式编程中常用的一个术语,意味着给定一个函数和一个输入值,您将始终收到相同的输出。 这就是说在函数中没有使用外部状态。

    该答案在价值和副作用方面定义了参照透明度,但它完全忽略了含义。

    这是一个函数,在第二个定义下不是透明的:

    var x = 0
    
    func changeX() -> Int {
        x += 1
        return x
    }
    

    它读取一些外部状态,改变它,然后返回值。 它不需要输入,每次调用时都会返回一个不同的值,并且它依赖于外部状态。 咩。 大不了。

    给定一个正确的指称语义,它仍然是透明的。

    为什么? 因为你可以用另一个具有相同语义含义的表达式替换它。

    现在,该函数的语义更加混乱。 我不知道如何定义它。 它与状态转换有关,给定一个状态s和一个产生新状态s'函数s' ,这个表示可能看起来像这样,尽管我不知道这在数学上是否正确:

    ⟦changeX⟧ ∷ λs → (s → s')
    

    是对的吗? 我没有线索。 Strachey想出了命令式语言的指称语义,但它很复杂,我还不明白。 然而,通过建立外延语义学,他确立了命令式语言与功能性语言一样透明。 为什么? 因为数学意义可以精确描述。 一旦你知道了某物的精确数学含义,你可以用其他含义相同的术语来代替它。 所以,尽管我不知道changeX函数的真正语义是什么,但我知道如果我有另一个具有相同语义含义的术语,我可以将另一个术语替换为另一个术语。

    那么Date.now怎么样?

    我对这个功能一无所知。 我甚至不确定它来自哪种语言,尽管我怀疑它可能是Javascript。 但谁在乎。 它的指称语义是什么? 这是什么意思? 你可以在不改变程序含义的情况下插入它的位置?

    丑陋的事实是,我们大多数人都没有线索! 指称语义并不是广泛使用的开始,命令式编程语言的指称语义非常复杂(至少对我来说 - 如果你觉得这很容易,我很乐意向你解释它)。 采取任何包含超过20行非重要代码的命令式程序,并告诉我它的数学意义是什么。 我挑战你。

    相比之下,Haskell的指称语义非常简单。 我对Haskell知之甚少。 我从来没有在ghci中搞过任何编码,但是它的强大之处在于,它的语法比我知道的任何其他语言更紧密地跟踪语义。 作为一个纯粹的,严格的函数式语言,语法就在语法的表面上。 语法由定义含义的数学概念定义。

    实际上,语法和语义紧密相关,功能程序员已经开始将这两者混为一谈。 (我虚心地提出这个观点并等待反弹。)这就是为什么你会从FPers获得有关价值而不是意义的参照透明度的定义。 像Haskell这样的语言,两者几乎没有区别。 由于没有可变状态,每个函数都是纯函数,因此您只需查看函数评估时产生的值,并基本确定其含义。

    在某种程度上,新时代的FPer对参照透明度的解释可能也比我上面总结的更有用。 这是不容忽视的。 毕竟,如果我上面写的是正确的,那么所有具有指称语义的指令都是透明的。 没有这样的东西是非引用透明的函数,因为每个函数都有数学意义(虽然它可能是模糊的,很难定义),并且您可以始终用另一个具有相同含义的术语替换它。 那有什么好处?

    嗯,有一个原因是有好处的。 让我们知道,我们不知道我们所做的背后的数学问题。 就像我上面所说的,我不知道Date.now的指称语义是什么,或者它在数学意义上是什么意思。 它是否透明? 是的,我相信它是可以的,因为它可以被具有相同语义的另一个函数替代。 但我不知道如何评估该函数的语义,因此其参考透明度对我来说是程序员无用的。

    因此,如果我从中学到了一点,那就是关注是否符合“参照透明度”的某些定义,以及更多关于如何使用小的,数学上可组合的方法制作程序的问题即使我能理解,也具有精确语义的部分。

    链接地址: http://www.djcxy.com/p/80583.html

    上一篇: Is Date.now referential transparent?

    下一篇: Pure function in Scala in the context of class methods, and closures