【问题标题】:Why does ($ 3) have signuature (a -> b) -> b?为什么 ($ 3) 有签名 (a -> b) -> b?
【发布时间】:2021-09-20 08:58:03
【问题描述】:

Learn you a Haskell中,给出了下面的例子:

map ($ 3) [(4+), (10*), (^2), sqrt]
[7.0,30.0,9.0,1.7320508075688772]  

但是,我不明白为什么会这样。

函数的签名是

Prelude> :info ($)
($) :: (a -> b) -> a -> b
Prelude> :t ($ 3)
($ 3) :: Num a => (a -> b) -> b

但是,-> 是左结合运算符,所以$ :: (a -> b) -> a -> b 实际上是((($ :: (a-b))-> a)-> b),所以($ 3) 中的3 不应该对应(a->b) 函数,它是第一个“变量”函数$?即为什么($ 3)是签名(a -> b) -> b的函数

来源:http://learnyouahaskell.com/higher-order-functions

【问题讨论】:

  • 不要使用多态值 3,尝试使用具体的值,例如 ($ False)($ ())

标签: haskell partial-application partialfunction


【解决方案1】:

但是,-> 是左结合运算符。

->右关联 运算符。在Learn You a Haskell documentation 中,它说:

首先,注意类型声明。之前,我们不需要括号,因为-> 是自然右结合的

($)($ 3) 的类型签名的更详细版本是:

($) :: (a -> b) -> (a -> b)
($ 3) :: Num a => (a -> b) -> b

我们可以将类型构造函数写成更规范的形式:

($) :: (->) ((->) a b) ((->) a b)
($ 3) :: Num a => (->) ((->) a b) b

因此这意味着$ 接受一个带有签名a -> b 的函数,因此返回一个带有签名a -> b 的函数,因此它基本上充当id,除了它限制了函数的输入(和不只是任何类型),并且您可以轻松使用$,而(`id` 3) 不是那么优雅。

如果我们因此将3 应用于f $ 3 运算符,我们知道f 将具有签名f :: a -> b,而3 将具有Num a => a 类型。如果我们使用($ 3) 进行运算符分段,我们将3 作为“第二个”参数传递(在Haskell 中,每个函数都只有一个参数,但在这里我们将它分配给($) f 的结果参数,这意味着($ 3) 的类型是Num a => (a -> b) -> b

【讨论】:

  • 但如果是这样的话,以下内容何时有效:f :: Int -> Float -> Float; f x y = y; :t (f 2); Float -> Float
  • 因为(f 2)不是 运算符切片。 (f 2) 就是 f 2,所以我们用参数 2 调用 f,并且由于 f 的类型为 f :: Int -> (Float -> Float),因此它返回一个 Float -> Float 函数。
  • @Our: no 当你做($ 3),那么它被解释为\x -> $ x 3,但是($) 3只是使用3作为参数。见section of an infix operatorf 不是(中缀)运算符,$ 是。
  • 换句话说,(/ 2) 将除以二,而 (/) 2 将除以该函数的参数,div 2 也将除以我们仍然缺少的参数。
  • 为了完整起见,(`f` 2.0)(2 `f`) 作为运算符分段的示例。将函数名称放在反引号中可以让您将其视为运算符。
猜你喜欢
  • 2014-03-29
  • 2021-10-06
  • 1970-01-01
  • 2010-12-08
  • 1970-01-01
  • 2011-05-30
  • 1970-01-01
  • 2018-06-27
  • 2012-07-03
相关资源
最近更新 更多