By George Z. August 12, 2021
函子(Functor)
Haskell 中的函子是一种可以映射的不同类型的函数表示。 它是实现多态的高级概念。 根据 Haskell 开发者的说法,List、Map、Tree 等所有类型都是 Haskell Functor 的实例。Functor 是一个内置类,其函数定义类似于
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
应用函子(Applicative Functor)
Applicative Functor 是一个普通的 Functor,具有 Applicative Type Class 提供的一些额外功能。使用 Functor,我们通常将现有函数映射到其中定义的另一个函数。 但是没有任何方法可以将 Functor 中定义的函数映射到另一个 Functor。 这就是为什么我们有另一个工具叫做 Applicative Functor。 这种映射功能是由 Control 模块下定义的 Applicative Type 类实现的。 这个类只为我们提供了两种方法:一种是纯方法,另一种是 <*>。
以下是 Applicative Functor 的类定义
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
根据实现,我们可以使用两种方法映射另一个 Functor:“Pure”和“<*>”。 “Pure” 方法应该采用任何类型的值,并且它将始终返回该值的 Applicative Functor。下面的例子展示了 Applicative Functor 是如何工作的
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"
Monoids
我们都知道 Haskell 以函数的形式定义了一切。 在函数中,我们可以选择将输入作为函数的输出。 这就是幺半群。Monoid 是一组函数和运算符,其中输出与其输入无关。 让我们以一个函数 (*) 和一个整数 (1) 为例。 现在,无论输入是什么,其输出都将保持不变。 也就是说,如果将一个数乘以 1,您将得到相同的数。
这是类型类定义
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} 是幺半群的完美例子。