纯函数式编程中的 "value" 是什么?

What is "value" in pure functional programming?

纯函数式编程中的值是什么?

看到一句话我问自己这些问题:

Task(or IO) has a constructor that captures side-effects as values.

我们如何测试某物是否是一个值?不变性是充分条件吗?

更新: 我正在使用 Scala。

What constitutes a value in pure functional programming?

背景

函数式编程中没有突变。因此,代码如

case class C(x: Int)

val a = C(42)
val b = C(42)

将等同于

case class C(x: Int)

val a = C(42)
val b = a

因为,在 函数式编程中,如果 a.x == b.x,那么我们将有 a == b。也就是说,a == b会比较里面的值来实现。

然而,Scala 并不纯粹,因为它允许变异,如 Java。在这种情况下,当我们声明 case class C(var x: Int) 时,上面的两个片段之间不等价。实际上,执行 a.x += 1 后记不会影响第一个片段中的 b.x,但会影响第二个片段,其中 ab 指向同一个对象。在这种情况下,有一个比较 a == b 比较对象 references,而不是它的内部整数值。

是很有用的。

当使用 case class C(x: Int) 时,Scala 比较 a == b 表现得更接近纯函数式编程,比较整数值。对于常规(非 case)类,Scala 会比较对象引用,从而打破两个片段之间的等价关系。但是,Scala 并不纯粹。相比之下,在 Haskell

data C = C Int deriving (Eq)
a = C 42
b = C 42

确实等同于

data C = C Int deriving (Eq)
a = C 42
b = a

因为 Haskell 中没有 "references" 或 "object identities"。请注意,Haskell 实现可能会在第一个片段中分配两个 "objects",而在第二个片段中只分配一个对象,但由于在 Haskell 中无法区分它们,因此程序输出将相同。

回答

Is a function a value ? (then what it means when equating two function: assert(f==g). For two function that is equivalent but defined separately => f!=g, why not they work like 1==1)

是的,函数是纯函数式编程中的值。

上面,当你提到"function that is equivalent but defined separately"时,你假设我们可以比较这两个函数的"references"或"object identities"。在纯函数式编程中我们不能。

对于所有可能的参数 x,纯函数式编程应该比较使 f == g 等同于 f x == g x 的函数。当 x 只有几个值时,这是可行的,例如如果 f,g :: Bool -> Int 我们只需要检查 x=True, x=False。对于具有无限域的函数,这要困难得多。例如,如果 f,g :: String -> Int 我们不能检查无限多个字符串。

理论计算机科学(可计算性理论)也证明不存在比较两个函数的算法String -> Int,即使是低效算法也不存在,即使我们可以访问两个函数的源代码也不存在。由于这个数学原因,我们必须接受函数是无法比较的值。在 Haskell 中,我们通过 Eq 类型类来表达这一点,声明几乎所有标准类型都是可比较的,函数除外。

Is an object with methods a value ? (for example, IO{println("")})

是的。粗略地说,"everything is a value",包括 IO 操作。

Is an object with setter methods and mutable states a value ? Is an object with mutable states and works as a state machine a value ?

纯函数式编程中没有可变状态。

setter 最多可以生成一个 "new" 具有修改字段的对象。

是的,对象将是一个值。

How do we test if it is a value, is that immutable can be a sufficient condition to be a value ?

在纯函数式编程中,我们只能拥有不可变数据。

在非纯函数式编程中,我认为当我们不比较对象引用时,我们可以调用大多数不可变对象"values"。如果 "immutable" 对象包含对可变对象的引用,例如

case class D(var x: Int)
case class C(c: C)
val a = C(D(42))

那么事情就比较棘手了。我想我们仍然可以调用 a "immutable",因为我们不能改变 a.c,但我们应该小心,因为 a.c.x 可以被改变。 根据意图,我认为有些人不会将 a 称为不可变的。我不认为 a 是一个值。

为了让事情变得更加混乱,在不纯的编程中,有些对象使用变异以有效的方式呈现 "pure" 接口。例如,可以编写一个纯函数,在 returning 之前将其结果存储在缓存中。当在同一个参数上再次调用时,它将 return 先前计算的结果 (这通常称为 memoization)。在这里,突变发生了,但从外部是观察不到的,我们最多只能观察到一个更快的实现。在这种情况下,我们可以简单地假设该函数是纯函数(即使它执行变异)并将其视为 "value".

与命令式语言形成鲜明对比。在命令式语言中,例如 Python,函数的输出是有向的。它可以分配给一个变量,显式返回,打印或写入文件。

当我在 Haskell 中编写函数时,我从不考虑输出。我从不使用 "return" 一切都有 "a" 价值。这叫做"symbolic"编程。 "everything" 表示 "symbols"。像人类语言一样,名词和动词代表某种东西。这就是他们的价值。 "Pete" 的 "value" 是皮特。 "Pete" 这个名字不是 Pete,而是 Pete 这个人的代表。函数式编程也是如此。最好的类比是数学或逻辑 当你做一页页的计算时,你是不是指挥每个函数的输出?您甚至可以在函数或表达式中将 "assign" 变量替换为它们的 "value"。

我将尝试通过将价值非价值.

的事物进行对比来解释什么是价值

粗略地说,是由求值过程产生的结构,对应于 无法进一步简化。


条款

首先,术语是什么?术语是可以评估的句法结构。诚然,这有点循环,让我们看几个例子:

  1. 常量文字是术语:

    42
    
  2. 应用于其他项的函数是项:

    atan2(123, 456 + 789)
    
  3. 函数文字是术语

    (x: Int) => x * x
    
  4. 构造函数调用是术语:

    Option(42)
    

对比一下:

  1. Class 声明/定义不是术语:

    case class Foo(bar: Int)
    

    即不能写

    val x = (case class Foo(bar: Int))
    

    这是非法的。

  2. 同样,特征和类型定义不是术语:

    type Bar = Int
    sealed trait Baz
    
  3. 与函数文字不同,方法定义不是术语:

    def foo(x: Int) = x * x
    

    例如:

    val x = (a: Int) => a * 2 // function literal, ok
    val y = (def foo(a: Int): Int = a * 2) // no, not a term
    
  4. 包声明和导入语句不是术语:

    import foo.bar.baz._ // ok
    List(package foo, import bar) // no
    

正常形式,值

现在,当 term 是什么时,希望更清楚一些,“不能再简化*”是什么意思?在理想化的函数式编程语言中,您可以定义什么范式,或者更确切地说,弱头范式是。本质上,如果没有归约规则可以,则术语处于(wh-)范式应用于术语以使其更简单。再举几个例子:

  1. 这是一个术语,但不是正规形式,因为它可以简化为42:

    40 + 2
    
  2. 这不是弱头范式:

    ((x: Int) => x * 2)(3)
    

    因为我们可以进一步计算为6.

  3. 这个 lambda 是弱头范式(它被卡住了,因为在提供 x 之前计算无法继续):

    (x: Int) => x * 42
    
  4. 这不是正规形式,因为它可以进一步简化:

    42 :: List(10 + 20, 20 + 30)
    
  5. 这是正常形式,无法进一步简化:

    List(42, 30, 50)
    

因此,

  • 42,
  • (x: Int) => x * 42,
  • List(42, 30, 50)

是值,而

  • 40 + 2,
  • ((x: Int) => x * 2)(3),
  • 42 :: List(10 + 20, 20 + 30)

不是值,而只是可以进一步简化的非规范化术语。


示例和非示例

我将逐一查看您的子问题列表:

Is a function a value

是的,像 (x: T1, ..., xn: Tn) => body 这样的东西在 WHNF 中被认为是卡住的术语,在函数式语言中它们实际上可以被表示,所以它们是值。

If so, what does it mean when equating two functions: assert(f == g) for two functions that are equivalent but defined separately => f != g, why don't they work as 1 == 1?

函数的可扩展性与某物是否有值的问题有些无关。在上面"definition by example"中,我只谈到了术语的形状,而不是关于那些术语上定义的一些可计算关系的存在/不存在。可悲的事实是,你甚至不能真正确定一个 lambda 表达式是否真的代表一个函数(即它是否对所有输入终止),而且众所周知,没有一种算法可以确定两个函数是否产生所有输入的相同输出(即扩展相等)。

Is an object with methods a value? (for example IO { println("") })

不太清楚你在这里问什么。对象没有方法。 Classes 有方法。如果您指的是方法调用,那么,不,它们是可以进一步简化的术语(实际上 运行 方法),因此它们不是值。

Is an object with setter methods and mutable state a value? Is an object with mutable state which works as a state machine a value?

纯函数式编程中没有这样的东西。

值为

  1. Immutable/Timeless
  2. 匿名
  3. 语义透明

42 的值是多少? 42. new Date()的"value"是什么? Date object at 0x3fa89c3。 42的身份是什么? 42.new Date()的身份是什么?正如我们在前面的例子中看到的,它是生活在这个地方的东西。它在不同的上下文中可能有许多不同的"values",但它只有一个身份。 OTOH,42 本身就足够了。询问 42 在系统中的位置在语义上毫无意义。 42 的语义是什么? 42 的量级。new Foo() 的语义是什么?谁知道呢

我要添加第四条标准(在野外的某些情况下看到这个,但在其他情况下看不到)是:值是 语言不可知的(我不确定前 3 个足以保证这一点,也不能保证这样的规则完全符合大多数人对价值含义的直觉。

价值观是

  • 函数可以作为输入,return作为输出,即可以计算,并且
  • 是一个类型的成员,即某个集合的元素,并且
  • 可以绑定变量,即可以命名

第一点确实是对某物是否有价值的关键测试。也许 value 这个词,由于条件反射,可能会立即让我们想到数字,但这个概念非常笼统。本质上,我们可以为函数提供和从函数中获取的任何东西都可以被视为一个值。数字、字符串、布尔值、classes 的实例、函数本身、谓词,甚至类型本身,都可以是函数的输入和输出,因此也是值。

IO monad 是这个概念的普遍性的一个很好的例子。当我们说 IO monad models side-effects as values 时,我们的意思是一个函数可以将副作用(例如 println)作为输入,并将 return 作为输出。 IO(println(...))println 动作的 效果 的想法与动作的实际执行分开,并允许将这些效果视为第一个 class 可以使用与任何其他值(例如数字)相同的语言工具计算的值。