首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Haskell:试图理解fmap (+)的类型(1)

Haskell:试图理解fmap (+)的类型(1)
EN

Stack Overflow用户
提问于 2019-12-07 08:29:19
回答 2查看 99关注 0票数 3

我试图理解为什么fmap (+) (1)没有类型错误

我理解以下顺序:

代码语言:javascript
复制
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)不隐含函子,相反,我得到:

代码语言:javascript
复制
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的类型

代码语言:javascript
复制
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>
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-12-07 08:51:03

数字文字可以适合任何类型,只要它是数字类型(在类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,所以类型检查工作。

代码语言:javascript
复制
fmap (+) (1) :: (Functor f, Num a, Num (f a)) => f (a -> a)

需要上述约束Num (f a)才能允许1f a类型中进行解释。那么f必须是函子,因为fmap需要它,而且我们必须有Num a,因为(+)要求它的参数是数字的( (+)的参数具有a类型,其结果是a -> a类型,其中a必须是数字的)。由于返回类型的f (a -> a),我们再次获得(+)

关于fmap (+) id,这更简单。这里是id :: (->) a a,这是id :: f af = (->) a,它恰好是一个函子。对于这个函子,我们有一个fmap = (.),函数式合成算子。因此,fmap (+) id的意思是(.) (+) id,或简单地说是(+)(+) . id

票数 5
EN

Stack Overflow用户

发布于 2019-12-07 08:43:17

这是一个简单的结果,因为数字文本(如1 )是多态的--它们的类型是(Num a) => a。此外,没有固定的数值类型列表,因为Haskell允许您为任何类(包括Num )创建自己的实例。

因此,在适用这一原则时:

代码语言:javascript
复制
Prelude> :t  fmap (+)
fmap (+) :: (Functor f, Num a) => f a -> f (a -> a)

对于文本1,类型检查器只需要检查是否可以将1类型与输入类型f a统一。考虑到上面提到的1的多态类型,这显然是可能的,前提是有一个Num实例可以用于f a --就像f a1的有效类型一样。这解释了最后一种类型。

至于fmap (+) id,这是完全不同的。这里,GHC需要将f a (对于函子f和数值a)与a -> a统一,这导致了f唯一可能的选择,即“函数函子”((->) a) ( fmap只是组合)。f b在这里的意思是a -> b,所以f (a -> a)a -> a -> a

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59224275

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档