我有两个简单的例子:
1)xt
功能(这是什么?
Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
Prelude> :{
Prelude| f::Int->Int
Prelude| f x = x
Prelude| :}
Prelude> xt = fmap f // ?
Prelude> :t xt
xt :: Functor f => f Int -> f Int
Prelude> xt (+2) 1
3
2)xq
功能(通过组合)
Prelude> :{
Prelude| return x = [x]
Prelude| :}
Prelude> xq = return . f
Prelude> :t xq
xq :: Int -> [Int]
Prelude> :t return
return :: a -> [a]
xq
我通过作文return(f(x))
获得的功能。但这意味着什么:fmap f
,区别是什么?
(->) r
Functor
实例将fmap
定义为函数组合:
fmap f g = f . g
因此,xt (+2) == fmap f (+2) == f . (+2) == (+2)
(因为f
是Int
的恒等函数)。应用于 1,你会得到观察到的答案 3。
fmap
是由Functor
类型类定义的函数:
class Functor f where
fmap :: (a -> b) -> f a -> f b
它接受一个函数作为其参数,并返回一个"提升"到相关函子中的新函数。确切的定义由Functor
实例提供。以上是函数函子的定义;这里供参考的是一些更简单的列表和Maybe
:
instance Functor [] where
fmap = map
instance Functor Maybe where
fmap f Nothing = Nothing
fmap f (Just x) = Just (f x)
> fmap (+1) [1,2,3]
[2,3,4]
> fmap (+1) Nothing
Nothing
> fmap (+1) (Just 3)
Just 4
由于您可以将函子视为包含一个或多个值的框,因此函数函子的直觉是函数是包含将函数应用于其参数的结果的框。也就是说,(+2)
是一个包含某个值加 2 的框。(F)在该框上映射函数提供了一个框,其中包含将f
应用于原始函数的结果,即生成一个函数,该函数是f
与原始函数的组合。
xq = return . f
和xt = fmap f
都可以进行 eta 扩展:
xq x = (return . f) x = return (f x) = return x
现在它可以是eta签约的:
xq = return
第二个是
xt y = fmap f y = fmap (x -> x) y = fmap id y = id y = y
fmap
有类型:: Functor f => (a -> b) -> f a -> f b
所以fmap f
有类型:: Functor f => f Int -> f Int
,因为f :: Int -> Int
。从它的类型中我们可以看到fmap f
是一个函数,期望一个Int
,并产生一个Int
。
由于根据定义f x = x
Int
s,这意味着f = id
Int
s,其中id
是一个预定义的函数,其定义方式与f
相同(但通常,对于任何类型)。
然后通过函子定律(这就是我们需要了解的关于"函子">的全部信息)fmap id = id
等等xt y = y
,换句话说,它也id
- 但仅适用于Int
s,
xt = id :: Int -> Int
自然,xt (+2) = id (+2) = (+2)
.
附录:对于某物,成为"Functor"意味着它可以代替f
fmap id (x :: f a) = x
(fmap g . fmap h) = fmap (g . h)
因此所涉及的表达式有意义(即格式良好,即具有类型),并且上述方程成立(它们实际上是两个"函子定律")。