【问题标题】:Higher-order function and parentheses高阶函数和括号
【发布时间】:2015-01-29 16:31:23
【问题描述】:

根据https://wiki.haskell.org/Compose的compose类型,可以写成
compose :: [a -> a] -> (a -> a)compose :: [a -> a] -> a -> a

我认为这两种类型是不同的:前者接受一个函数列表并返回一个函数,后者接受一个函数列表和一个参数,然后最终返回一个值。

也就是说,当一个函数(高阶函数)将另一个函数作为参数或返回一个函数作为结果时,不应省略参数(结果)周围的括号,例如,如果 filter :: (a -> Bool) -> [a] -> [a] 删除括号,它的含义会改变。

我是对还是错?

【问题讨论】:

  • 不是专家,但我认为这两种情况是一样的。这样做的原因是柯里化(wiki.haskell.org/Currying
  • 如果你 curry compose 只给它第一个参数,那么每种情况下的结果类型是什么,例如功能列表?这有助于解释发生了什么吗?在柯里化之后的这两种情况下,如果你只提供一个参数(输入a)会发生什么?
  • 我鼓励您考虑您希望能够进行哪些观察来区分这两种类型,并尝试在 ghci 中进行这些观察。他们有什么不同吗? (我以这种方式鼓励你,因为当我了解这个空间中发生的事情的那一刻,我的头脑爆炸了,从那以后我一直无法离开 Haskell。)
  • 你错了——-> 是右联想,所以a -> foodiebar 自动变成(->) (a) (foodiebar)。函数也是如此:a -> b -> ca -> (b -> c)

标签: haskell


【解决方案1】:

这些都是一样的:

compose1 :: [a -> a] -> (a -> a)
compose1 [] = \x -> x
compose1 [f] = \x -> f x
compose1 (f1:f2:fs) = compose1 ((f2 . f1):fs)

compose2 :: [a -> a] -> a -> a
compose2 [] x = x
compose2 [f] x = f x
compose2 (f1:f2:fs) x = compose2 ((f2 . f1):fs) x

注意这些定义实际上是相同的,除了 lambda 从 = 的一侧移动到另一侧。实际上,您始终可以进行以下转换:

f x y z = <expr x y z>
f x y = \z -> <expr x y z>
f x = \y -> \z -> <expr x y z> = \y z -> <expr x y z>
f = \x -> \y -> \z -> <expr x y z> = \x y z -> <expr x y z>

这实际上是编译器对所有函数所做的。如果您使用-ddump-simpl 编译,您将看到核心代码转储,其中所有函数都是根据 lambda 定义的。这是因为 Haskell 使用的定律是

f x = <expr>

相当于

f = \x -> <expr>

可以认为 lambda 语法比使用显式参数定义函数更基本。


也就是说,当一个函数(高阶函数)将另一个函数作为参数或返回一个函数作为结果时,不应省略参数(结果)周围的括号,例如,如果 filter :: (a -&gt; Bool) -&gt; [a] -&gt; [a] 删除括号,它的含义会改变。

您认为不能从filter 的类型签名中删除括号是正确的,这是因为函数应用程序仅是右关联的。这意味着以下签名是等价的:

f :: a -> b -> c -> d -> e
f :: a -> (b -> (c -> (d -> e)))

这就是关联到右边的意思,你在添加嵌套括号时从右到左。但是,f 的签名等同于

-- Not equivalent!
f :: (((a -> b) -> c) -> d) -> e

仅仅是因为-&gt; 不是完全关联的运算符。例如,+ 你有

x + (y + z) = (x + y) + z

但是-&gt; 你有

x -> (y -> z) /= (x -> y) -> z

这类似于: 运算符,例如

1:2:3:4:[] == 1:(2:(3:(4:[])))
           /= (((1:2):3):4):[]

最后一个表达式不会进行类型检查,因为 1:2 类型错误,2 不是列表!

【讨论】:

    【解决方案2】:

    箭头是右关联的,所以[a -&gt; a] -&gt; a -&gt; a[a -&gt; a] -&gt; (a -&gt; a) 是等价的。

    如果您删除filter 类型签名中的括号,您会得到:

    a -> Bool -> [a] -> [a]
    a -> Bool -> ([a] -> [a])
    a -> (Bool -> ([a] -> [a]))
    

    ...这与正确的类型签名不一样。

    【讨论】:

      猜你喜欢
      • 2020-08-12
      • 1970-01-01
      • 1970-01-01
      • 2016-05-07
      相关资源
      最近更新 更多