在Haskell中,将函数作为Functor的实例感到困惑

26
Functor 中 fmap 的类型为:
fmap :: Functor f => (a -> b) -> f a -> f b

看起来,首先将函数 (a -> b) 应用于 f a 的参数,以创建类型为 b 的结果,然后将 f 应用于该结果,其结果是 f b。

Maybe a 为例:

 fmap show (Just 1)
 result is : Just "1"

同样意思的说法:

Just (show 1)

但是当 (->) 作为一个 Functor(在 Control.Monad.Instances 中)被使用时

import Control.Monad.Instances
(fmap show Just) 1
result is : "Just 1"

也就是说,首先应用Just,然后再应用show。在另一个例子中,结果相同:

 fmap (*3) (+100) 1
 result is 303

为什么不先使用*3,然后再使用+100

5个回答

34

(->) r(即函数)的fmap实例实际上就是组合。来自源代码本身

instance Functor ((->) r) where
    fmap = (.)

因此,在您的示例中,我们可以将fmap替换为(.),并进行一些转换。

fmap (*3) (+100) 1 => 
(.) (*3) (+100) 1  =>
(*3) . (+100) $ 1  => -- put (.) infix
(*3) (1 + 100)     => -- apply (+100)
(1 + 100) * 3         -- apply (*3)

也就是说,对于函数来说,fmap 的组合顺序是从右到左(与 (.) 完全相同,这是很合理的,因为它就是 (.) )。

换句话说(为了(double)确认!),我们可以使用类型签名:

-- general fmap
fmap :: Functor f => (a -> b) -> f a -> f b

-- specialised to the function functor (I've removed the last pair of brackets)
fmap :: (a -> b) -> (r -> a) -> r -> b 

因此,首先需要将类型r的值(第三个参数)通过r -> a函数转换为类型a的值,以便a -> b函数可以将其转换为类型b的值(结果)。


1
是的,fmap :: (a -> b) -> (r -> a) -> r -> b,这解释了很多,谢谢。 - 诺 铁
1
链接已经失效。现在链接为http://hackage.haskell.org/package/base-4.12.0.0/docs/src/GHC.Base.html#line-818 - rosshjb

27

在Functor中,fmap的类型是:

fmap :: Functor f => (a -> b) -> f a -> f b
似乎首先将函数(a->b)应用于f的参数a,以创建类型为b的结果,然后对其应用f,结果为f b。
这是fmap的类型,但你对这个类型的解释是错误的。 您似乎认为f a有一个参数,并且该参数的类型为a。
考虑xs :: [a]:
1.也许xs=[]。 2.也许xs=[x1]。 3.也许xs=[x1,x2]。 ...
类型f a是具有单个类型参数a的函子f。但是类型为f a的值不一定采用F x的形式,就像上面的第一个和第三个情况一样。
现在考虑fmap f xs:
1.也许fmap f xs=[]。 2.也许fmap f xs=[f x1]。 3.也许fmap f xs=[f x1,f x2]。 ...
我们甚至可能根本没有应用f(第一种情况)!或者我们可能多次应用它(第三个情况)。
我们所做的是将类型为a的元素替换为类型为b的元素。但是我们保留了更大的结构-没有添加新元素,没有删除元素,它们的顺序保持不变。
现在让我们考虑functor (c ->)。(请记住,functor仅采用一个类型参数,因此(->)的输入是固定的。) a>甚至包含吗?它可能根本不包含任何,但是当我们给它一个时,它可以从空气中神奇地制造出一个。但是fmap的结果具有类型c->b:我们只需要在呈现时提供一个
所以我们可以说fmap f x = \y -> f (x y)。
在这种情况下,我们按需应用——每次应用返回的函数时,也会应用。

2
是的,你的答案很棒!我犯了一个大错误。非常感谢你。 - 诺 铁
我将“类型参数”和具体参数混淆了。 - 诺 铁

21

需要被定义成那样以使得类型能够契合。正如你指出的一样,fmap的类型是:

fmap :: Functor f => (a -> b) -> f a -> f b

考虑函子 f((->) c) 的情况:

(注意: 实际上我们更想将其写成 (c ->),即从 c 转换为函数,但 Haskell 不允许这样做。)

那么 f a 实际上就是 ((->) c a),相当于 (c -> a),对于 f b 同理,因此我们有:

fmap :: (a -> b) -> (c -> a) -> (c -> b)

即,我们需要使用两个函数:

  • f :: a -> b
  • g :: c -> a

并构建一个新的函数

  • h :: c -> b

但这只有一种方法:你必须先应用 g 来获得类型为 a 的内容,然后再应用 f 来获得类型为 b 的内容,这意味着你必须 定义

instance Functor ((->) c) where
    fmap f g = \x -> f (g x)

或者更简洁地说,

instance Functor ((->) c) where
    fmap = (.)

6

fmap对于(->)被定义为fmap = (.)。 因此,(fmap f g) x等同于(f . g) x等同于f (g x)。 在您的情况下,(*3) ((+100) 1)等同于3 * (100 + 1),结果为303


1
要形成一个函数类型,您需要为(->)提供2种参数,即单个输入参数类型和返回类型。
Functor只能使用1种类型参数,因此必须确定输入参数类型(因为它是从左到右的第一个),这使得函数的返回类型成为Functor的类型参数。
因此,对于函数(Functor)a->b,您需要给fmap一个类型为b->xxx的函数ff而不是a->xxx才能工作,这意味着函数ff只能在a->b之后应用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接