Haskell中的函子是一種可以映射不同類型的函數表示。它是實現多態性的高級概念。根據Haskell開發人員,列表、映射、樹等所有類型都是Haskell函數的實例。
函子是一個內建的類,它的函數定義類似 −
class Functor f where
fmap :: (a -> b) -> f a -> f b
根據這個定義,可以得出這樣的結論:Functor是一個函數,它接受一個函數,比如fmap()
,然後返回另一個函數。在上面的例子中,fmap()
是函數map()
的一種通用表示。
在下面的示例中,我們將看到Haskell Functor是如何工作的。
main = do
print(map (subtract 1) [2,4,8,16])
print(fmap (subtract 1) [2,4,8,16])
在這裏,我們對一個列表使用map()
和fmap()
進行差集操作。可以觀察到,這兩個語句將生成列表[1,3,7,15]
的相同結果。
這兩個函數都調用了另一個subtract()
函數來生成結果。
[1,3,7,15]
[1,3,7,15]
那麼,map
和fmap
有什麼區別呢?區別在於它們的用法。Functor
使我們能夠在不同的數據類型中實現更多的泛函函數,比如“just”和“Nothing”。
main = do
print (fmap (+7)(Just 10))
print (fmap (+7) Nothing)
上述代碼段將在終端上產生以下輸出:
Just 17
Nothing
1. 應用函子
應用函子是一個正常的函子與一些額外的功能提供的應用 Type
類。
使用函子,我們通常將一個現有的函數與其中定義的另一個函數進行映射。但是沒有任何方法可以將定義在函子內部的函數映射到另一個函子。這就是為什麼要有另一個功能叫做應用函子(Applicative Functor)。這種映射功能由Control
模組下定義的應用程式Type
類實現。這個類只提供了兩個方法:一個是純方法,另一個是<*>
方法。
下麵是應用函子的類定義。
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
根據實現,可以使用“純”和“<*>
”兩種方法來映射另一個函子。“純”方法應該接受任何類型的值,並且總是返回該值的應用函式。
下麵的例子說明了應用函子的工作原理:
import Control.Applicative
f1:: Int -> Int -> Int
f1 x y = 2*x+y
main = do
print(show $ f1 <$> (Just 1) <*> (Just 2) )
這裏,在函數f1
的函數調用中實現了應用函數。程式將產生以下輸出:
"Just 4"
2. Monoids
眾所周知,Haskell以函數形式定義了所有內容。在函數中,可以選擇將輸入作為函數的輸出,這就是Monoid。
Monoid是一組函數和運算符,其中輸出與其輸入無關。我們以一個函數(*
)和一個整數(1
)為例。無論輸入是什麼,輸出將僅保持相同的數字。也就是說,如果將數字乘以1
,也將得到相同的數字。
下麵是Monoid的Type
類定義。
class Monoid m where
mempty :: m
mappend :: m -> m -> m
mconcat :: [m] -> m
mconcat = foldr mappend mempty
請看下麵的示例,以瞭解Monoid在Haskell中的使用。
multi:: Int->Int
multi x = x * 1
add :: Int->Int
add x = x + 0
main = do
print(multi 9)
print (add 7)
上面代碼將產生以下輸出:
9
7
這裏,函數multi
將輸入參數值與1
相乘。類似地,函數add
用0
加上輸入參數值。在這兩種情況下,輸出將與輸入相同。因此,函數{(*),1}
和{(+),0}
是monoids的例子。