2012-04-24 154 views
20
功能函子的实例

FMAP的函子类型是:困惑在Haskell

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

它看起来像,第一应用功能(一 - > b)至FA的参数创建的结果b型,然后应用F到它,并且结果是FB

使用也许例如:

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

相同的话说:

Just (show 1) 

但是当( - >)用作函子(在Control.Monad.Instances)

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

即,只要是第一应用,则显示被应用。在另一个例子中,结果是一样的:

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

为什么不* 3,再+100?

回答

22

FMAP的函子类型是:

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

它看起来像,第一应用功能(一 - > b)至FA 的参数来创建b型的结果,然后应用F到它,结果是fb

这是fmap的类型,但是您对该类型的含义的解释是错误的。

您似乎认为f a有一个参数,并且该参数的类型为a

考虑xs :: [a]

  • 也许xs = []
  • 也许xs = [x1]
  • 也许xs = [x1, x2]
  • ...

类型f a是一个算符f使用单一类型的参数a。但是的值的类型f a不一定采取F x的形式,如您从上面的第一个和第三个案例可以看到的那样。

现在考虑fmap f xs

  • 也许fmap f xs = []
  • 也许fmap f xs = [f x1]
  • 也许fmap f xs = [f x1, f x2]
  • ...

我们不一定在所有的(第一种情况)适用f!或者我们可能会多次应用(第三种情况)。

我们要做的就是更换a型的东西,用b类型的东西。但是我们保留了较大的结构 - 没有添加新的元素,没有删除元素,他们的顺序保持不变。


现在让我们来想一下函子(c ->)。 (请记住,仿函数只有一个类型参数,所以(->)的输入是固定的。)

是否有一个c -> a甚至包含一个a?它可能根本不包含任何a,但当我们给它一个c时,它可以以某种方式幻化出来。但fmap的结果有c -> b类型:我们只需提供一个b即可,当我们看到c时。我们可以说fmap f x = \y -> f (x y)

在这种情况下,我们按需要应用f ---每当我们返回的函数被应用时,f也被应用。

+1

是的,你的答案很棒!我犯了一个大错误。非常感谢你。 – 2012-04-24 13:54:37

+0

我混淆了“类型参数”与一个具体参数 – 2012-04-24 14:19:52

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

请记住,f也可以是一个类型的构造函数。我认为这个意思是(对于最简单的情况),该函数使用函数a包装在f中,并将其转换为b类型的东西,并使用函数a -> b包装在f中。

在第二个例子中,你在做(fmap show Just) 1。这是类型

Prelude> :t fmap show Just 
fmap show Just :: (Show a, Functor ((->) a)) => a -> String 

的这与前一个

Prelude> :t fmap show (Just 1) 
fmap show (Just 1) :: Maybe String 

所不同的是在第一个Just是一种类型的构造,而Just 1是一种类型的一个实例。 fmap是适当的通用的,因此它对两者都有意义。

+1

在“请记住,f也可以是一个类型构造函数”您可能意思是“必须”而不是“也可以”。 – 2012-04-24 12:35:49

+0

'Just'不是一个类型构造函数,它是一个值或数据构造函数。 '也许'是一个类型构造函数。 – 2012-04-24 21:15:58

5

fmap对于(->)的定义类似fmap = (.)。 因此,(fmap f g) x(f . g) xf (g x)。在你的情况(*3) ((+100) 1),这等于3 * (100 + 1)其结果在303

26

(->) r(即函数)的fmap实例实际上只是组合。从the source itself

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的功能组成它们从右到左(完全一样作为(.),这是明智的,因为它是(.))。

看另一种方式(for(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 -> a函数被变换成a(类型的值)r类型(第三个参数)的值,从而使a -> b功能可以将其转换为b类型的值(结果)。

+0

谢谢,这是一个很好的清晰定义! – 2012-04-24 09:03:36

+1

是的,fmap ::(a - > b) - >(r - > a) - > r - > b,这个很解释,谢谢 – 2012-04-24 13:33:37

14

需要被定义为使这些类型成为可能。正如你指出的,fmap类型是:

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

让我们考虑的情况下,当函子f((->) c)

注:我们会真正喜欢写为(c ->),即功能从c,但哈斯克尔不允许我们这样做)。

然后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 = (.) 
0

为了形成一个功能类型,需要2个参数( - >),即单输入参数类型和返回类型。

一个Functor只能取一个类型参数,所以你必须确定输入参数类型(因为它是从左到右的第一个参数),这使得函数的返回类型是类型参数函子。

因此,对于函数(Functor)a-> b,除了a-> xxx之外,您需要给fmap一个类型为b-> xxx的函数ff来工作,这意味着函数ff只能在a-> b是适用的。