【问题标题】:Function Types Bound to Arguments绑定到参数的函数类型
【发布时间】:2015-02-22 05:54:26
【问题描述】:

Brent Yorgey 的出色 UPenn Haskell course 呈现:

fmap2 :: Functor f => (a -> b -> c) -> (f a -> f b -> f c)
fmap2 h fa fb = undefined

hfafb 的类型细分为:

h  :: a -> b -> c
fa :: f a
fb :: f b

我不清楚为什么h 指的是整个函数(a -> b -> c)

为什么h 不能引用afa 不能引用(b -> c)

(a -> b -> c) 中的括号有区别吗?

编辑

考虑leftaroundaboutcomment

对于只是阅读本文而不知道所指特定课程的任何人:fmap2 不能用该签名定义。

其实应该是liftA2 :: Applicative a => (a->b->c) -> (f a->f b->f c)

【问题讨论】:

  • 对于只是阅读本文而不知道所指特定课程的任何人:fmap2 可以使用该签名进行定义。它实际上需要是liftA2 :: Applicative a => (a->b->c) -> (f a->f b->f c)

标签: haskell


【解决方案1】:

是的,括号的区别与您所说的完全一样。因为(->)right-associative,而不是mathematically associative,所以不能按照您建议的方式拆分函数箭头左侧的括号表达式:

(a -> b) -> (f a -> f b) /= a -> b -> f a -> f b

在这方面,-> 运算符与求幂运算符 ^ 类似,在符号上是右关联但 not mathematically associative

(2 ^ 2) ^ (2 ^ 2) /= 2 ^ 2 ^ 2 ^ 2
4       ^ 4       /= 2 ^ (2 ^ (2 ^ 2))
256               /= 2 ^ (2 ^ 4)
256               /= 2 ^ 16
256               /= 65536

(取幂的类比不是我自己发明的;函数类型是“指数类型”,就像(a, b) 是“乘积类型”和Either a b 是“求和类型”一样。但请注意, a -> b 类似于b ^ a,而不是a ^ bSee this blog post for an example-heavy explanation;也类似于this answer gives a mathematical overview of type algebra。)

fmap2 的明显奇怪之处在于该类型看起来需要 one 参数,但定义看起来需要 三个。对比这个版本,至少在我看来它更像类型签名:

fmap2 :: Functor f => (a -> b -> c) -> (f a -> f b -> f c)
fmap2 h = \fa fb -> undefined

现在我们有了一个不错的“单参数”fmap2 h = ...,右侧有一个“双参数”lambda。诀窍在于,在 Haskell 中,这两个表达式是等价的[*]:Haskell Report 表示带有 LHS 上的参数的“函数”形式在“语义上等价于”一个简单的 lambda 模式绑定。

您还可以重写类型以消除箭头 右侧 侧的括号,同样因为-> 是右关联的:

   (a -> b -> c) -> (f a -> f b -> f c) 
== (a -> b -> c) -> f a -> f b -> f c

就像

   (2 ^ 2 ^ 2) ^ (2 ^ 2 ^ 2)
== (2 ^ 2 ^ 2) ^ 2 ^ 2 ^ 2

[*]:它们在语义上是等价的,但是当使用 GHC 编译时,它们的性能 特性可能并且有时确实会有所不同。 GHC 的优化器对f x = ...f = \x -> ... 的处理方式不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 2019-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多