我的意思是,来自definition:
中最不固定的点
fix f
是函数f
换句话说:
最不明确的
x
,f x = x
。
任何 nullary 类型的最小定义值为undefined
。这里有一些含糊之处,因为undefined
可能意味着"会抛出错误" (更好)或"永远不会终止" (更糟糕的)。正如推理和试验所示,fix (+1)
和fix pred :: Word
似乎都没有接近终止(即使pred 0
是一个错误),所以更糟糕的一个"永远不会终止&#34 ;可能总是在这两种选择之间进行选择。 (可以逃脱fix
的是无聊的const 1
,但稍后就可以了。)
这不是应用fix
的有用方法。
应用fix
的有用方法是:
fix (\f x -> if x > 7 then f (x - 1) else x)
- 也就是说,使用fix
神奇地生成递归函数。 (这永远不会让我惊讶。)这可以被视为在两个函数之间进行选择:\f x -> f (x - 1)
和\_ x -> x
,其中一个函数永远不会评估它的第一个绑定变量。这是一个危险的玩具,因为我们仍然可以通过意外地将>
转移到<
或-
到{{1}来获得一个不会终止其域的一半的函数}。
所以,不知何故,这两个函数:
+
- 后者是&#34; 最少定义的&#34;。如果我们眯着眼睛,我们实际上可以认出我们无聊的同事f1 = \f x -> f (x - 1)
f2 = \_ x -> x
,只是const
&#39; d。
现在,这是违反直觉的。 flip
实际上更具容错能力,因此,从某种意义上说,它是为更多输入值定义的,而不是f2
。 (特别是,这是让它逃脱f1
)无限循环的原因。具体而言,fix
是针对f2
加(f, x)
的所有输入f1
对定义的。沿着同一条线,(undefined, x)
是所有一元函数最容错的。
那么,我现在如何理解定义?
问题的一些理由
有this answer nearby这种const 1
提供了与此问题中提出的直觉不同的直觉。它还强调,为了充分理解,必须转到关于指称语义的外部教程。
我想得到一个答案,要么支持,要么解释这里提出的直觉的错误,而且,如果领域理论真的落后于评论中提出的草书排序,至少包括一些经验法则,允许(但有限的)但实际应用领域理论。我想看到的问题的一个特定部分是fix
能函数是否总能在常数函数和约简函数上分解,这些类的定义是什么?功能是。
我的愿望是建立一个实用的,实用的答案,建立有用且安全的fix
编码的递归,并辅以可靠的数学推理。
答案 0 :(得分:6)
在Haskell中,函数是纯粹的。他们接受输入和产量输出。因此,出现了一个自然的问题:那些不会终止的功能呢?
f x = 1 + f x
此函数会将解释器锁定在无限循环中。但在数学上,我们必须说它&#34;返回&#34;某事,或者它不是一个功能。我们称这是特别的&#34;出了问题&#34;重视&#34;底部&#34;值,我们用符号⊥
表示它。
因此,从数学上讲,Haskell Int
类型包含每个整数,以及一个额外的特殊元素:⊥
。我们称之为包含⊥
a&#34;提升的类型&#34;类型,几乎所有你在Haskell中使用的类型都被解除了。 [1] 事实证明,无限循环并不是调用⊥
的唯一方法。我们也可以通过&#34;崩溃&#34;其他方面的翻译。您将看到的最常见的方法是undefined
,这是一个内置函数,可以通过一般错误来暂停程序。
但还有另一个问题。具体来说,停止问题。如果⊥
应该表示无限循环和其他不可预测的问题,那么我们无法在Haskell中编写某些函数。例如,以下伪代码是荒谬的。
doesItHalt :: a -> Bool
doesItHalt ⊥ = False
doesItHalt _ = True
这将检查结果值是否为⊥
,这将解决暂停问题。显然,我们不能在Haskell中做到这一点。那么我们可以在Haskell中定义哪些函数?我们可以定义那些与定义性排序有关的单调性。我们将⊑
定义为此排序。我们说⊥
是定义最少的值,因此对于所有⊥ ⊑ x
都是x
。 ⊑
是部分排序,因此无法比较某些元素。在1 ⊑ 1
期间,1 ⊑ 2
或 2 ⊑ 1
并非如此。在纯英语中,我们说1
肯定是低于或等同于1
(显然;它们是相同的值),但它并不是&{有道理地说1
或多或少地定义为2
。他们只是......不同的价值观。
在Haskell中,我们只能定义与此排序有关的单调函数。因此,对于所有值a
和b
,如果a ⊑ b
然后f a ⊑ f b
,我们只能定义函数。我们上面的doesItHalt
失败了,例如,⊥ ⊑ "foo"
但f ⊥ = False
和f "foo" = True
。正如我们之前所说,两个完全定义但不相等的值无法比较。所以这个功能无法工作。
简单来说,我们定义这种方式的原因是因为,当我们&#34;检查&#34;一个使用模式匹配的值,它作为一个断言,必须至少定义它足以让我们看到我们匹配的部分。如果不是,那么我们总是获得⊥
,因为程序会崩溃。
值得注意的是,在我们讨论fix
之前,有部分定义的&#34;&#34;值。例如,1 : ⊥
(在Haskell中,我们可以将其写为1 : undefined
)是一个列表,其第一个元素已定义,但列表的尾部未定义。从某种意义上说,这个值是'#34;更明确的&#34;而不是一个简单的⊥
,因为我们至少可以模式匹配并提取第一个值。所以我们会说⊥ ⊑ (1 : ⊥) ⊑ (1 : [])
。因此,我们最终得到了#34;定义的层次结构&#34;。
现在,fix
表示它返回定义最少的固定点。函数f
的固定点是x
的值,x = f x
。让我们试试看几个例子,看看我们是否可以弄清楚为什么会这样说。让我们定义一个函数
f 0 = 1
f x = x
这个功能有很多固定点。对于除{0}以外的任何x
,f x = x
。通过我们最少定义的&#34;原则,哪一个会被退回?实际上,⊥
会f ⊥
将返回⊥
。如果我们将undefined
传递给f
,则第一次模式匹配会导致程序崩溃,因为我们正在将未定义的值与0
进行比较。因此⊥
是一个定点,因为它是最不可能定义的值,它将由fix f
返回。在内部,fix
通过无限地将函数应用于自身来工作,因此这有一定的意义。应用f (f (f ...))
将继续尝试将内部参数与0
进行比较,并且永远不会终止。现在让我们尝试不同的功能。
g x = 0
将此函数无限地应用于自身0
。事实证明,fix g = 0
。为什么在这种情况下返回0
而不是⊥
?事实证明,⊥
无法成为此功能的固定点。 g ⊥ = 0
。由于参数从未被检查过,Haskell是一种非严格的语言,g
将返回0
,即使您传递undefined
或error "Oops!"
或一些荒谬的无限递归值一个论点。因此,g
的仅固定点,即使是提升类型,也是0
,因为g 0 = 0
。因此,0
确实是g
中最不明确定义的固定点。
因此,总而言之,我们定义了一个数学框架,以便严格描述某些功能不会终止的概念。 &#34;至少定义&#34;这只是一种非常数学上精确的说法,fix
不会对他们的论证中总是严格的函数起作用。如果f ⊥
将返回⊥
,则fix f
也将返回⊥
。
*大多数答案都是从the wiki page on Denotational Semantics转述并总结出来的。我鼓励阅读该页面以获取更多信息;它的编写对非数学家来说是非常容易理解的,并且非常有用。
[1]一些原始类型未被解除。例如,GHC特定的Int#
是一个不包含⊥
的整数类型,在某些地方内部使用。