Haskell函子(functor)

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]

那麼,mapfmap有什麼區別呢?區別在於它們的用法。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相乘。類似地,函數add0加上輸入參數值。在這兩種情況下,輸出將與輸入相同。因此,函數{(*),1}{(+),0}是monoids的例子。


上一篇: Haskell輸入和輸出 下一篇: Haskell Monads