Haskell中具有相同参数的其他函数中的函数求值

Haskell中具有相同参数的其他函数中的函数求值,haskell,Haskell,考虑下面的玩具示例: someNum :: Int someNum = 42 funcCommon :: Int -> Int funcCommon x = x + 1 func1 :: Int -> (Int -> Int) -> Int func1 x f = f x func2 :: Int -> (Int -> Int) -> Int func2 x f = f x f1 = func1 someNum funcCommon f2 =

考虑下面的玩具示例:

someNum :: Int
someNum = 42

funcCommon :: Int -> Int
funcCommon x = x + 1

func1 :: Int -> (Int -> Int) -> Int
func1 x f = f x

func2 :: Int -> (Int -> Int) -> Int
func2 x f = f x

f1 = func1 someNum funcCommon 
f2 = func2 someNum funcCommon 

main = do
  print f1
  print f2

相同的参数funcCommonsomeNum作为参数应用于func1和func2,因此我们得到一种情况,func1和func2分别声明(是不同的函数),但最终都使用相同的参数调用相同的函数。那么,funcommon someNum实际得到了多少次评估?结果是否得到重用,并且只有一次对funcCommon someNum的求值?

严格来说,该语言没有定义是求值一次、两次还是37次,甚至没有定义对某个特定次数求值的含义。它所能保证的只是遵守严格的规则,即

f1, f2 :: [Int]
f1 = repeat 1
f2 = error "evil"

main = do
  print f1
  print f2
您可以确保不会触发错误,因为打印
f1
实际上会永远吐出
1
个数字,因此
f2
的计算次数正好为零

对于某些特定情况,编译器实际上可以在编译时完成
f1
f2
的所有计算,并且在运行时根本不会对它们进行“评估”


在实践中,您应该预期会发生两次评估。确认两个函数做相同的事情是一个困难的问题,因此编译器通常不会麻烦。

理论上,它只需要计算一次。实际上,这是一个实现细节。默认情况下,Haskell没有表格,因此它通常会对函数进行两次计算。虽然它有可能得到优化。在OP的例子中,f1和f2不仅做同样的事情,而且用同样的方式做同样的事情。编译器合并相同实现的函数是很简单的。GHC就是这样做的。当然,在这种特殊情况下,它们也足够简单,可以完全内联到main中。@n.m.GHC是否合并它们?当然,消除相同实现的函数是微不足道的,但对于非平凡代码来说,它显然也是无用的。如果有什么区别的话,我会说用它来触发警告是有意义的。有趣的是,消除那些仅仅是等价的东西,但这确实是不可行的。我认为这只是发生在CSE考试中。