IO ,下面的IO的实例,可以把 getLine 看做是一个去真实世界中拿取字串的盒子, 而 applicative functor 表达式会创造一个比较大的盒子,这个大盒子会派两个盒子去终端拿取字串,并把结果串接起来放进自己的盒子中。
--IO 的 Applicative instance instance Applicative IO where pure = return a <*> b = do f <- a x <- b return (f x) -- 实例 将输入的两个字符串合并 (++) <$> getLine <*> getLine aa bb > "aabb" Applicative Functor 的 (->) r 形态(->) r 形态定义
instance Applicative ((->) r) where pure x = (\_ -> x) f <*> g = \x -> f x (g x)用 pure 将一个值包成 applicative functor 的时候,他产生的结果永远都会是那个值
将两个 applicative functor 喂给 <*> 可以产生一个新的 applicative functor
接着综合使用上面的知识,来看一下实际应用applicative的几种方式。相比起functor,applicative functor要更强大和灵活。
-- 左结合形式, 第一项必须为含有函数的functor,右边全部为functor pure (\x y z -> x+ y +z) <*> Just 3 <*> Just 4 <*> Just 5 > Just 12 [(+3),(*2)] <*> [1,2] > [4,5,2,4] -- fmap(<$>) 形式,第一项为普通函数,右边都为functor (+) <$> Just 1 <*> Just 2 > Just 3 (\x y z -> x + y +z) <$> [1,2] <*> [2,3] <*> [4,5] > [7,8,8,9,8,9,9,10] -- (<$>) (->) r 形式,全部为普通函数,用单个参数调用执行 (\x y z -> [x,y,z]) <$> (3+) <*> (*100) <*> (`div`2) $ 2 > [5,200,1] Applicative Functor 辅助函数
liftA2
只是applicative的套用函数而已,当然还有3个参数的版本 liftA3,而 liftA 则等价于 fmap
sequenceA
当套用在函数上时,sequenceA 接受装有一堆函数的list,并回传一个回传list的函数。当我们有一串函数,想要将相同输入都喂给它们并查看结果的时候,sequenceA非常好用。
当使用在 I/O action 上的时候,sequenceA 跟 sequence 是等价的。他接受一串 I/O action 并回传一个 I/O action,这个 I/O action 会计算 list 中的每一个 I/O action,并把结果放在一个 list 中