我想了解为什么 fmap (+) (1)
我确实理解以下顺序:
Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
Prelude> :t (+)
(+) :: Num a => a -> a -> a # like a-> b with b = a -> a
Prelude> :t fmap (+)
fmap (+) :: (Functor f, Num a) => f a -> f (a -> a)
Prelude> :t fmap (+) (Just 1)
fmap (+) (Just 1) :: Num a => Maybe (a -> a) # f=Maybe is implied by the Just constructor
Prelude>
我预计 fmap (+) (1)
会出现类型错误,因为 (1) 没有隐含仿函数,而是得到:
Prelude> :t (1)
(1) :: Num p => p
Prelude> :t (+)
(+) :: Num a => a -> a -> a
Prelude> :t fmap (+)
fmap (+) :: (Functor f, Num a) => f a -> f (a -> a)
Prelude> :t fmap (+) (1)
fmap (+) (1) :: (Functor f, Num a, Num (f a)) => f (a -> a) ## why ??
Prelude>
这是为什么?
同样,我不明白 fmap (+) id
的类型:
Prelude> :t fmap (+)
fmap (+) :: (Functor f, Num a) => f a -> f (a -> a)
Prelude> :t id
id :: a -> a
Prelude> :t fmap (+) id
fmap (+) id :: Num a => a -> a -> a ## why no error ?
Prelude>
最佳答案
数字字面值可以适合任何类型,前提是它是数字类型(在 Num
类中)。
GHC 在一个开放世界的假设下工作,即使一个类型现在不属于一个实例,它也可能在未来。这是因为可以编写一个新模块并在那里声明一个实例,而我们需要单独编译。
对于数字类型,这意味着即使一个类型现在不是数字,以后也可能是。
假设我们写reverse 1
。它看起来不对,因为 reverse
需要一个列表,而 1
不是一个列表。或者是吗?即使 [a]
现在不是数字,将来也可能是,因此 reverse 1
的类型是 Num [a] => [a]
,而不是类型错误。当然,在正常情况下我们不会有一个 Num [a]
实例,但 GHC 不能假设。
在您的具体示例中,fmap
想要一个 f a
并且您传递了 (1)
,这与 1
。在这里,这个数字文字被实例化为 Num (f a) => f a
,这样类型检查就可以工作了。
fmap (+) (1) :: (Functor f, Num a, Num (f a)) => f (a -> a)
需要上述约束 Num (f a)
以允许 1
在类型 f a
中被解释。那么 f
必须是一个仿函数,因为 fmap
需要它,我们必须有 Num a
因为 (+)
需要它的参数是数字((+)
的参数类型为 a
,其结果类型为 a -> a
,其中 a
必须是数字)。由于 (+)
的返回类型,我们再次得到 f (a -> a)
的结果。
关于fmap(+)id
,这个比较简单。这里的id::(->) a a
,也就是id::f a
,其中f = (->) a
,正好是一个仿函数。对于这个仿函数,我们有 fmap = (.)
,函数组合运算符。因此,fmap (+) id
表示 (.) (+) id
或 (+) 。 id
就是 (+)
。
关于Haskell:试图理解 fmap 的类型(+)(一),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59224275/