2012-04-24 218 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是適用的。