Scala “什么是”呢;“价值”;在纯函数式编程中?

Scala “什么是”呢;“价值”;在纯函数式编程中?,scala,haskell,functional-programming,terminology,Scala,Haskell,Functional Programming,Terminology,在纯函数式编程中,什么构成了价值 看到一句话后,我问自己这些问题: Task(或IO)有一个构造函数,它将副作用捕获为值 函数是值吗? 如果是这样,那么当将两个函数相等时,它意味着什么:assert(f==g)。对于两个等效但单独定义的函数=>f!=g,为什么它们不作为1==1 具有方法的对象是否为值?(例如IO{println(“”)}) 具有setter方法和可变状态的对象是值吗 作为状态机工作的具有可变状态的对象是值吗 我们如何测试某物是否是一个值?不变性是一个充分条件吗 更新:

在纯函数式编程中,什么构成了价值

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

Task
(或
IO
)有一个构造函数,它将副作用捕获为

  • 函数是值吗?
    • 如果是这样,那么当将两个函数相等时,它意味着什么:
      assert(f==g)
      。对于两个等效但单独定义的函数=>
      f!=g
      ,为什么它们不作为
      1==1
  • 具有方法的对象是否为值?(例如
    IO{println(“”)}
  • 具有setter方法和可变状态的对象是值吗
  • 作为状态机工作的具有可变状态的对象是值吗
我们如何测试某物是否是一个值?不变性是一个充分条件吗

更新: 我用的是Scala

在纯函数式编程中,什么构成了价值

背景 在纯函数式编程中,没有变异。因此,代码

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类C(var x:Int)
时,上述两个代码片段之间没有等价性。实际上,执行
a.x+=1
afterwords不会影响第一个片段中的
b.x
,但会影响第二个片段中的
a
b
指向同一对象。在这种情况下,比较对象引用(而不是其内部整数值)的比较
a==b
非常有用

使用
case class C(x:Int)
时,Scala比较
a==b
的行为更接近于纯函数编程,比较整数值。对于常规(非
case
)类,Scala会比较打破两个代码段之间等价性的对象引用。但是,Scala也不是纯粹的。相比之下,在哈斯克尔

数据C=C整数推导(Eq)
a=C 42
b=C 42
确实相当于

case class C(x: Int)

val a = C(42)
val b = a
数据C=C整数推导(Eq)
a=C 42
b=a
因为Haskell中没有“引用”或“对象标识”。请注意,Haskell实现可能会在第一个代码段中分配两个“对象”,在第二个代码段中只分配一个对象,但由于在Haskell内部无法区分它们,因此程序输出将是相同的

答复 函数是值吗?(那么当两个函数相等时意味着什么:assert(f==g)。对于两个等价但单独定义的函数=>f!=g,为什么它们不像1==1那样工作呢)

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

上面,当您提到“等效但单独定义的函数”时,您假设我们可以比较这两个函数的“引用”或“对象标识”。在纯函数式编程中,我们不能

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

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

具有方法的对象是否为值?(例如,IO{println(“”})

对。粗略地说,“一切都是价值”,包括IO操作

具有setter方法和可变状态的对象是值吗? 具有可变状态并作为状态机工作的对象是值吗

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

最多,setter可以使用修改后的字段生成一个“新”对象

是的,对象将是一个值

我们如何测试它是否是一个值,不可变是否是成为值的充分条件

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

在不纯函数编程中,我认为当我们不比较对象引用时,我们可以将大多数不可变对象称为“值”。如果“不可变”对象包含对可变对象的引用,例如

case class D(var x: Int)
case class C(c: C)
val a = C(D(42))
那么事情就更棘手了。我想我们仍然可以称a为“不可变的”,因为我们不能改变a.c,但是我们应该小心,因为a.c.x可以变异。 根据意图,我认为有些人不会将
a
称为不可变的。我不认为<代码> a <代码>是一个值。

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

atan2(123, 456 + 789)
(x: Int) => x * x
Option(42)
case class Foo(bar: Int)
val x = (case class Foo(bar: Int))
type Bar = Int
sealed trait Baz
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
import foo.bar.baz._ // ok
List(package foo, import bar) // no
40 + 2
((x: Int) => x * 2)(3)
(x: Int) => x * 42
42 :: List(10 + 20, 20 + 30)
List(42, 30, 50)