Wei*_*Lin 11 haskell functional-programming scala terminology
什么构成纯函数式编程的价值?
看到一句话后我问自己这些问题:
Task
(或IO
)有一个构造函数,可以将副作用捕获为值.
assert(f == g)
.对于两个相同但单独定义的函数=> f != g
,为什么它们不起作用1 == 1
?IO { println("") }
) 我们如何测试某些东西是否有价值?不变性是一个充分条件吗?
更新:我正在使用Scala.
在纯函数式编程中,什么构成值?
在纯函数式编程中,没有任何突变。因此,例如
case class C(x: Int)
val a = C(42)
val b = C(42)
Run Code Online (Sandbox Code Playgroud)
将等同于
case class C(x: Int)
val a = C(42)
val b = a
Run Code Online (Sandbox Code Playgroud)
因为在纯函数式编程中,如果a.x == b.x
,那么我们将有a == b
。也就是说,a == b
将比较内部的值来实现。
但是,Scala不是纯的,因为它允许像Java这样的突变。在这种情况下,当我们声明时,上面的两个代码片段之间就没有等效性case class C(var x: Int)
。确实,执行a.x += 1
后记词不会影响b.x
第一个代码段,但会影响第二个代码段,其中a
并b
指向同一对象。在这种情况下,进行比较a == b
比较对象引用而不是其内部整数值很有用。
使用时case class C(x: Int)
,Scala比较的a == b
行为更接近纯函数式编程,比较整数值。对于常规(非case
)类,Scala会比较对象引用,以打破两个摘要之间的等效性。但是,同样,Scala并不是纯粹的。相比之下,在Haskell
case class C(x: Int)
val a = C(42)
val b = C(42)
Run Code Online (Sandbox Code Playgroud)
确实等于
case class C(x: Int)
val a = C(42)
val b = a
Run Code Online (Sandbox Code Playgroud)
因为在Haskell中没有“引用”或“对象标识”。请注意,Haskell实现可能会在第一个代码段中分配两个“对象”,而在第二个代码段中仅分配一个对象,但是由于无法在Haskell内部将它们区分开,因此程序输出将相同。
函数是值吗?(然后,当等于两个函数时,这意味着什么:assert(f == g)。对于两个等效但单独定义的函数=> f!= g,为什么它们不像1 == 1那样工作)
是的,函数是纯函数编程中的值。
上面,当您提到“功能相同但分别定义的功能”时,您假设我们可以比较这两个功能的“引用”或“对象标识”。在纯函数式编程中,我们不能。
纯粹的函数式编程应比较所有可能的参数都f == g
等于的f x == g x
函数x
。当只有几个值时这是可行的x
,例如如果f,g :: Bool -> Int
我们只需要检查x=True, x=False
。对于具有无限域的函数,这要困难得多。例如,如果f,g :: String -> Int
我们不能检查无限多个字符串。
理论计算机科学(可计算性理论)也证明了没有算法可以比较两个函数String -> Int
,甚至没有效率低的算法,即使我们可以访问这两个函数的源代码也是如此。由于这个数学原因,我们必须接受函数是无法比较的值。在Haskell中,我们通过Eq
类型类表达这一点,指出几乎所有标准类型都是可比较的,函数是例外。
具有方法的对象是值吗?(例如,IO {println(“”)})
是。粗略地说,“一切都是价值”,包括IO操作。
具有setter方法和可变状态的对象是值吗?具有可变状态并作为状态机工作的对象是值吗?
在纯函数式编程中没有可变状态。
设置员充其量只能产生带有已修改字段的“新”对象。
是的,对象将是一个值。
我们如何测试它是否是值,不可变是否可以成为值的充分条件?
在纯函数式编程中,我们只能有不可变的数据。
我认为在不纯函数式编程中,当我们不比较对象引用时,可以将大多数不可变对象称为“值”。如果“不可变”对象包含对可变对象的引用,例如
case class D(var x: Int)
case class C(c: C)
val a = C(D(42))
Run Code Online (Sandbox Code Playgroud)
那么事情就比较棘手了。我猜我们仍然可以称其为a
“不可变的”,因为我们不能改变a.c
,但是我们应该小心,因为它a.c.x
会被突变。根据意图,我认为有些人不会将其称为a
不变的。我不认为a
这是一个价值。
为了使事情变得更加混乱,在不纯编程中,有些对象使用突变来有效地呈现“纯”界面。例如,可以编写一个纯函数,该函数在返回之前将其结果存储在缓存中。在相同的参数上再次调用该方法时,它将返回先前计算的结果(通常称为memoization)。在这里,发生了突变,但是从外部观察不到,最多只能观察到更快的实现。在这种情况下,我们可以简单地假装该函数是纯函数(即使它执行了变异)并将其视为“值”。
我会尽力解释什么值是用的东西都是对比它并不值。
粗略地说,值是评估过程中产生的结构,对应于无法进一步简化的术语。
条款
首先,什么是术语?术语是可以评估的句法结构。诚然,这有点循环,所以让我们看一些示例:
常量文字是术语:
42
Run Code Online (Sandbox Code Playgroud)应用于其他术语的函数是以下术语:
atan2(123, 456 + 789)
Run Code Online (Sandbox Code Playgroud)函数文字是术语
(x: Int) => x * x
Run Code Online (Sandbox Code Playgroud)构造函数调用是以下术语:
Option(42)
Run Code Online (Sandbox Code Playgroud)与此相比:
类声明/定义不是术语:
case class Foo(bar: Int)
Run Code Online (Sandbox Code Playgroud)
也就是说,你不能写
val x = (case class Foo(bar: Int))
Run Code Online (Sandbox Code Playgroud)
这将是非法的。
同样,特征和类型定义也不是术语:
type Bar = Int
sealed trait Baz
Run Code Online (Sandbox Code Playgroud)与函数文字不同,方法定义不是术语:
def foo(x: Int) = x * x
Run Code Online (Sandbox Code Playgroud)
例如:
val x = (a: Int) => a * 2 // function literal, ok
val y = (def foo(a: Int): Int = a * 2) // no, not a term
Run Code Online (Sandbox Code Playgroud)软件包声明和导入语句不是术语:
import foo.bar.baz._ // ok
List(package foo, import bar) // no
Run Code Online (Sandbox Code Playgroud)范式,值
现在,当希望在某种程度上更清楚地理解术语是什么时,“不能进一步简化*”是什么意思?在理想的函数式编程语言中,您可以定义普通形式或弱头普通形式。如果没有简化规则可以简化,那么该术语将以(wh-)正常形式出现。
这是一个术语,但不是正常形式,因为它可以简化为42
:
40 + 2
Run Code Online (Sandbox Code Playgroud)这不是弱头正常形式:
((x: Int) => x * 2)(3)
Run Code Online (Sandbox Code Playgroud)
因为我们可以进一步评估它6
。
此lambda处于弱头范式(被卡住,因为在x
提供an之前无法进行计算):
(x: Int) => x * 42
Run Code Online (Sandbox Code Playgroud)这不是正常形式,因为它可以进一步简化:
42 :: List(10 + 20, 20 + 30)
Run Code Online (Sandbox Code Playgroud)这是正常形式,无法进一步简化:
List(42, 30, 50)
Run Code Online (Sandbox Code Playgroud)从而,
42
, (x: Int) => x * 42
, List(42, 30, 50)
是价值,而
40 + 2
, ((x: Int) => x * 2)(3)
, 42 :: List(10 + 20, 20 + 30)
不是值,而只是可以进一步简化的非标准化术语。
例子与非例子
我将逐一逐一浏览您的子问题列表:
函数是值吗
是的,类似(x: T1, ..., xn: Tn) => body
的东西被认为是WHNF中的固定术语,在功能语言中它们实际上可以表示,因此它们是值。
如果是这样,那么当等于两个函数时是什么意思:
assert(f == g)
对于两个等效但单独定义的函数=>f != g
,为什么它们不起作用1 == 1
?
函数扩展性与某物是否为值的问题无关。在上面的“示例定义”中,我仅讨论了术语的形式,而不是讨论根据这些术语定义的某些可计算关系的存在/不存在。可悲的事实是,您甚至无法真正确定lambda表达式是否真正表示一个函数(即,它是否对于所有输入都终止),并且还众所周知,无法找到一种算法来确定两个函数是否产生了该函数。所有输入的输出相同(即,在扩展上相等)。
具有方法的对象是值吗?(例如
IO { println("") }
)
不太清楚您在这里要问什么。对象没有方法。类有方法。如果您的意思是方法调用,那么它们不是可以进一步简化(通过实际运行方法)的术语,因此它们不是值。
具有setter方法和可变状态的对象是值吗?具有可变状态的对象(充当状态机)是否是值?
在纯函数式编程中没有这样的事情。
归档时间: |
|
查看次数: |
523 次 |
最近记录: |