fmap::Functorf=>(a->b)->fa->fbfmap(+1)(Just1)-- Just 2, type = Maybe, constructor = Justfmap(*2)[1,2,3,4]-- [2,4,6,8], type = []fmap(+2)(Left2)-- Left 4, type = Either, constructor = Leftfmap(+2)(Right2)-- Right 4, type = Either, constructor = Right fmap(*2)(1,2)-- (1,4), type = ((,) a)
Functor 定律
fmap需要满足以下约束条件:
12
fmapid=idfmap(f.g)=fmapf.fmapg
不满足以上定律的ADT,如果声明了满足Functor,用fmap方式操作的时候可能产生错误的行为。
fmap 的局限性
在Haskell中,所有的函数都可以视为只带一个参数,其余的参数可以通过 currying 来视为以第一个参数为参数,返回以其它剩余参数为参数的一个函数,即 a -> b -> c 也可看作一个以 a 为参数返回一个 b -> c的函数的函数。考虑如下的例子:
因为传入fmap的函数参数为: (^) :: (Num a, Integral b) => a -> b -> a, 数据类型为 list, 于是,fmap的结果则是一个list类型,其中的元素参数为一个函数。对于这个list再次调用fmap, 那么对应的函数就是一个基于函数的函数 (lambda描述为 \f -> f 2), 结果就是将对应的lambda 函数作用于list中的每一个函数,生成最终的list 数据。这里的2次fmap调用得到的结果始终是一个list, 只不过每次的list具体数据类型有所不同。
:typepure(+)-- (Applicative f, Num a) => f (a-> a -> a):type(pure(+)<*>)-- (Applicative f, Num a) => f a -> f (a -> a):type(pure(+)<*>Just3)-- (Applicative f, Num a) => Maybe (a -> a):type(pure(+)<*>Just3<*>)-- (Applicative f, Num a) => Maybe b -> Maybe b
对于对个参数的情况,Applicative可以通过<*>来依次传入对应的参数,并应用于对应的变换函数,最后将得到的结果用Functor的构造生成新的ADT变量。从上边的例子其实可以得出,pure f <*> x = fmap f x;因此Applicative定义了<$>来简化书写,即最后一个例子等价于
ghci>:info<*classFunctorf=>Applicativefwhere...(<*)::fa->fb->fa-- Defined in Control.Applicativeinfixl4<*ghci>Just2<*Just1Just2ghci>:t(Just(+2)<*)(Just(+2)<*)::Numa=>Maybeb->Maybe(a->a)ghci>:t(Just(+2)<*Just2)(Just(+2)<*Just2)::Numa=>Maybe(a->a)ghci>:t(Just(+2)<*Just2<*>Just1)(Just(+2)<*Just2<*>Just1)::Numb=>Maybebgci>Just(+2)<*Just2<*>Just1Just3
可见<* 是忽略函数右侧的其它参数,返回左侧。*>则和其相反:
12
ghci>Just(+2)*>Just1Just1
Haskell有如下的const函数,因此 <**>可以有 <*> 和const来实现:
1234
ghci>:infoconstconst::a->b->a-- Defined in GHC.Basef*>g=flipconst<$>f<*>gf<*g=const<$>f<*>g
f<$>a=fmapfapuref<*>x=fmapfx=f<$>x-- we can further writeg::(a->b->c)fg<*>fa<*>fb<*>fc=pureg<*>fa<*>fb<*>fc=g<$>fa<$>fb<*>fc--exampleghci>letfabcd=2*a+3*b+4*c+5*dghci>:typeff::Numa=>a->a->a->a->aghci>f<$>Just1<*>Just2<*>Just3<*>Just4Just40ghci>Just(f1234)Just40