5

我正在努力学习和理解 Haskell 的设计。我目前正在使用 Lambda / 匿名函数,我想知道。

为什么不是 Eq 类的函数类型实例?

Prelude> (\z -> z + 5) == (+5)

在这个问题上,我想知道是不是因为 z 可以是任何东西,甚至可能是所有 lambda 函数中的自由变量,所以制作 Eq 类型的 lambda 函数将是一个设计缺陷。

为什么不是类型类 Show 的函数类型实例?

Prelude> (\q -> q - 2)

我感谢任何澄清。

提前谢谢了!

4

4 回答 4

9

这些功能是相同还是不同:

dbl1 :: Int -> Int
dbl1 x = x + x

dbl2 :: Int -> Int
dlb2 x = 2 * x

?

对于某些函数,编译器“容易”看到它们包含相同的逻辑。但是大多数功能都很难比较。然后有些功能在逻辑上不同,但行为相同 - 类似dbl1dbl2以上。因此,您必须做出选择,要么根据每个可能的值测试它们,要么决定它们不相等。在大多数情况下,前者是完全不切实际的。后者绝对是不可取的或不直观的。现在,考虑到这个问题已经太难解决了,然后投入IO...

于 2013-04-05T14:39:09.583 回答
6

我觉得 Dave 和 Peter 都没有足够强调的一个关键概念是:两个函数相等当且仅当 (a) 它们具有相同的类型,并且 (b) 对于你可以给它们的每个可能的参数,它们都产生同样的结果

现在,有了这一点,答案Eq就是他们所说的。Peter 指出,Eq函数的实例需要能够分析两个任意函数并正确确定它们是否对任何两个输入产生相同的结果。正如戴夫所指出的,这在数学上实际上是不可能的。任何试图比较任意函数的检查器对于某些函数都会失败——这意味着它会在某些情况下挂起或产生错误的答案。

然而,你会看到 Haskell 程序员一直在谈论函数相等,但大多数时候他们的意思是人类已经证明了某些两个函数是相等的。例如,函数组合运算符定义如下:

(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \x -> f (g x)

我们现在可以证明对于所有可能的h :: c -> d,g :: b -> ch :: a -> b, f . (g . h) == (f . g) . h。在这种情况下,这很容易,我们只需根据 的定义扩展表达式,(.)就可以得到相同的结果:

f . (g . h) = f . (\y -> g (h y))          -- expand `g . h` by definition
            = \x -> f ((\y -> g (h y)) x)  -- expand `f . (\y -> g (h y))`
            = \x -> f (g (h x))            -- apply the inner lambda

(f . g) . h = (\y -> f (g y)) . h
            = \x -> (\y -> f (g y)) (h x)
            = \x -> f (g (h x))

但这只能针对精心挑选的功能来完成,而计算机通常不能很好地或可靠地做到这一点。对于您编写的任何尝试测试任意函数是否相等的程序,都会有一些函数产生错误的答案或永远循环。

于 2013-04-05T16:37:54.567 回答
4

哥德尔的不完备性定理意味着Eq函数的任何实例必须要么给出不准确的结果,要么有时返回 ⊥。这不是我们对Eq实例的期望(至少对于有限数据)。

show应该提供评估其输入的 Haskell 源代码。这在编译 Haskell 程序时很尴尬,因为现在您必须为每个函数保留一份源代码的副本,从而使可执行文件膨胀,即使Show函数的实例从未使用过

可以为Show违反此规则的函数提供实例,例如通过始终返回"{-function-}"或(对于某些类型)返回函数的类型。Haskell 的早期版本做到了。但有人认为打破这条规则并不是一个好主意。

于 2013-04-05T14:38:38.170 回答
1

我喜欢每个人的答案……他们似乎有道理。在这个阶段,我无法想象为什么函数没有默认设置为 Eq 和 Show 的实例。这只是一些实验,可能会为您提供自己尝试的想法:

Prelude> :set -XFlexibleInstances
Prelude> instance Eq (Int -> Int) where x == y = map x [0..10] == map y [0..10]
Prelude> ((\z -> z+5) :: Int -> Int) == ((+5) :: Int -> Int)
True
Prelude> instance Show (Int -> Int) where show x = show (zip [0..10] (map x [0..10]))
Prelude> (\q -> q-2) :: (Int -> Int)
[(0,-2),(1,-1),(2,0),(3,1),(4,2),(5,3),(6,4),(7,5),(8,6),(9,7),(10,8)]
于 2013-04-05T23:50:36.803 回答