何时在Haskell中需要lambda形式?

Mat*_*ick 8 scheme closures haskell higher-order-functions

我是Haskell的新手,也是函数式编程的新手.在其他(除了Haskell)语言中,lambda表单通常非常有用.

例如,在Scheme中:

(define (deriv-approx f)
  (lambda (h x)
    (/ (- (f (+ x h)
          (f x)
       h)))
Run Code Online (Sandbox Code Playgroud)

将创建一个闭包(在函数f上)以逼近导数(值x,间隔为h).但是,由于部分应用,在Haskell中似乎不需要使用lambda形式:

deriv-approx f h x = ( (f (x + h)) - (f x) ) / h
Run Code Online (Sandbox Code Playgroud)

在Haskell中需要lambda形式的一些例子是什么?

编辑:用'lambda form'替换'closure'

C. *_*ann 12

我将给出两个稍微间接的答案.

首先,请考虑以下代码:

module Lambda where

derivApprox f h x = ( (f (x + h)) - (f x) ) / h
Run Code Online (Sandbox Code Playgroud)

我已经编译了这个,同时告诉GHC转储一个中间表示,它大致是用作编译过程一部分的Haskell的简化版本,以获得这个:

Lambda.derivApprox
  :: forall a. GHC.Real.Fractional a => (a -> a) -> a -> a -> a
[LclIdX]
Lambda.derivApprox =
  \ (@ a) ($dFractional :: GHC.Real.Fractional a) ->
    let {
      $dNum :: GHC.Num.Num a
      [LclId]
      $dNum = GHC.Real.$p1Fractional @ a $dFractional } in
    \ (f :: a -> a) (h :: a) (x :: a) ->
      GHC.Real./
        @ a
        $dFractional
        (GHC.Num.- @ a $dNum (f (GHC.Num.+ @ a $dNum x h)) (f x))
        h
Run Code Online (Sandbox Code Playgroud)

如果你看过凌乱的注释和冗长,你应该能够看到编译器已经将所有内容都转换为lambda表达式.我们可以认为这表明您可能不需要手动执行此操作.

相反,让我们考虑一下你可能需要lambdas的情况.这是一个使用折叠来组成函数列表的函数:

composeAll :: [a -> a] -> a -> a
composeAll = foldr (.) id
Run Code Online (Sandbox Code Playgroud)

那是什么?看来不是lambda!事实上,我们也可以采取其他方式:

composeAll' :: [a -> a] -> a -> a
composeAll' xs x = foldr (\f g x -> f (g x)) id xs x
Run Code Online (Sandbox Code Playgroud)

这不仅充满了lambdas,它还对主函数提出了两个论点,而且还适用foldr于所有这些参数.比较上面的类型foldr,(a -> b -> b) -> b -> [a] -> b以及 显然它需要三个参数,但上面我们已经应用了四个!更不用说累加器函数有两个参数,但我们在这里有三个参数lambda.当然,诀窍在于两者都返回一个带有单个参数的函数; 我们只是在现场应用这个论点,而不是在周围玩弄lambdas.

希望所有这些都使你相信这两种形式是等价的.Lambda表格永远不是必需的,或者可能总是必要的,因为谁可以说出差异?


ham*_*mar 6

两者之间没有语义差异

f x y z w = ...
Run Code Online (Sandbox Code Playgroud)

f x y = \z w -> ...
Run Code Online (Sandbox Code Playgroud)

表达式样式(显式lambdas)和声明样式之间的主要区别是语法风格.重要的一种情况是你想要使用一个where条款:

f x y = \z w -> ...
   where ... -- x and y are in scope, z and w are not
Run Code Online (Sandbox Code Playgroud)

通过用命名本地函数或部分应用程序替换它们,可以在不使用显式lambda的情况下编写任何Haskell程序.

另请参阅:声明与表达式样式.

  • 那些倾向于没有lambda生活方式的人也可能会利用`pointfree`程序,另外还可以作为lambdabot在IRC上的`@ pl`命令,轻松提升表达的优雅. (2认同)
  • @CA McCann然后让一些人对普通人来说难以理解;) (2认同)