首页 > 解决方案 > Haskell:试图理解 fmap 的类型(+)(1)

问题描述

我试图理解为什么没有类型错误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>

标签: haskell

解决方案


数字文字可以适合任何类型,只要它是数字类型(在 class 中Num)。

GHC 在开放世界假设下工作,即使一个类型现在不属于一个实例,它也可能在未来。这是因为可以编写一个新模块并在那里声明一个实例,我们需要单独编译。

对于数字类型,这意味着即使一个类型现在不是数字,它也可能在以后出现。

假设我们写reverse 1. 它看起来不对,因为reverse需要一个列表,而1不是一个列表。或者是吗?即使[a]现在不是数字,也可能是将来,因此类型reverse 1Num [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在 type 中进行解释f a。Thenf必须是函子,因为fmap需要它,并且我们必须Num a因为(+)需要它的参数是数字(的参数(+)有类型a,它的结果有类型a -> a,其中a必须是数字)。由于返回类型为 ,我们f (a -> a)再次得到结果(+)


关于fmap (+) id,这个比较简单。这里id :: (->) a a,就是id :: f awhere f = (->) a,恰好是一个函子。对于这个函子,我们有 that fmap = (.),即功能组合运算符。因此,fmap (+) id意味着(.) (+) id,或者(+) . id简单地说(+)


推荐阅读