Applicative is a special form of functor, in our previoius post we have already discussed the functor with ((->) r);
now, we will see the beefed up functor, which is in the Applicator typeclass,and you will import the Control.Applicative module.
and we will first examine how we can apply partially applied function to the Appilcative functors. so first let's see the use example of the functor as such
ghci> :t fmap (++) (Just "hey")
fmap (++) (Just "hey") :: Maybe ([Char] -> [Char])
ghci> :t fmap compare (Just 'a')
fmap compare (Just 'a') :: Maybe (Char -> Ordering)
ghci> :t fmap compare "A LIST OF CHARS"
fmap compare "A LIST OF CHARS" :: [Char -> Ordering]
ghci> :t fmap (\x y z -> x + y / z) [3,4,5,6]
fmap (\x y z -> x + y / z) [3,4,5,6] :: (Fractional a) => [a -> a -> a]
so, what if we apply the functor mapping on the "multi-parameter" functions over functors?? first let 's see the example as below;.
ghci> let a = fmap (*) [1,2,3,4]
ghci> :t a
a :: [Integer -> Integer]
ghci> fmap (\f -> f 9) a
[9,18,27,36]
as we can see, when map (*) on the [1,2,3,4] (you can treat the [1,2,3,4] as the [] functor on the nmbers of 1,2,3,4 respectively); then you will get back a list of functors, and then you apply the function \f ->f to each of them.
this is how the Applicative typeclass is defined, it is defined in the Control.Applicative module.
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
to be an Applicative, you first has to be a Functor,and this has been manifested by the type constraint. the pure function servers as the "applicative functor instance" it means take value of any type and return an applicative functor with that value in it.
the interesting part of the Applicative functor is the <*> function. the type of the functor is f (a -> b) -> f a -> f b ;it reminds us that it resembles that of (a -> b) -> f a -> f b, which is the type of fmap function. so it means it can take a function and a funcotr and apply the function inside the functor... It basically performs a "do and run"...
so, if we were to implement the Applicative functor on Maybe type. here it is.
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
the reasoning of the Just is ignored. Let's see how we can apply that Just to the Maybe type.
ghci> Just (+3) <*> Just 9
Just 12
ghci> pure (+3) <*> Just 10
Just 13
ghci> pure (+3) <*> Just 9
Just 12
ghci> Just (++"hahah") <*> Nothing
Nothing
ghci> Nothing <*> Just "woot"
Nothing
With normal functors, you can just map a function over a functor and then you can't get the result out in any general way, even if the result is a partially applied function. Applicative functors, on the other hand, allow you to operate on several functors with a single function. Check out this piece of code:
ghci> pure (+) <*> Just 3 <*> Just 5
Just 8
ghci> pure (+) <*> Just 3 <*> Nothing
Nothing
ghci> pure (+) <*> Nothing <*> Just 5
Nothing
So, you have observed, the Applicative function gives that ability to chain something together. so as in "ability to operte on several functors with a single function.", <*> is left associative.
This can be even handy and apparent if we use the <$> notation, which has the folowing signatjure.
(<$>) :: (Functor f) => (a -> b) -> f a -> f b
f <$> x = fmap f x
it just takes the (fmap) function as the infix operator,it does a lifting, so that instead of writing pure f <*> x <*> y <*> you can write fmap f x <*> y <*> ...and which can be even simplified as f <$> x <*> y <*> ...
here is the some examples that leveerage the <$> notation and other to performs some transformation.
ghci> (++) <$> Just "johntra" <*> Just "volta"
Just "johntravolta"
so, in a nutshell, the applicative function will take a applicative and returns an applicative. Hoiw cool is that..
Some times,you nee dto instruct to the just function to let it know the types. see below.
ghci> pure "Hey" :: [String]
["Hey"]
ghci> pure "Hey" :: Maybe String
Just "Hey"
Let's take another look of the <*> on list ony. it should have a type of (<*>) :: [a -> b] -> [a] -> [b]. , and it is implemented as List comporehension. and what we will get is a list comprehension behavior, where every possible function from the left list to every possible value of the right list, the resulting list has every possible combination of applying a function from left list to a value in the right one.
ghci> [(*0),(+100),(^2)] <*> [1,2,3]
[0,0,0,101,102,103,1,4,9]
and yet another like this:
ghci> [(+),(*)] <*> [1,2] <*> [3,4]
[4,5,5,6,3,4,6,8]
we will compare that of a list comprehension to that of applicative ways .
so, instead of
ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]]
[16,20,22,40,50,55,80,100,110]
we can do this:
ghci> (*) <$> [2,5,10] <*> [8,10,11]
[16,20,22,40,50,55,80,100,110]
and you can easily chain operation to the Applicative functors, such as
ghci> filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11]
[55,80,100,110]
IO can be made a instance of the Applicative functor, let's see how it is implemented.
instance Applicative IO where
pure = return
a <*> b = do
f <- a
x <- b
return (f x)
f <*> were specialized for IO it would have a type of(<*>) :: IO (a -> b) -> IO a -> IO b. It would take an I/O action that yields a function as its result and another I/O action and create a new I/O action from those two that, when performed, first performs the first one to get the function and then performs the second one to get the value and then it would yield that function applied to the value as its result. We used do syntax to implement it here. Remember, do syntax is about taking several I/O actions and gluing them into one, which is exactly what we do here.
However, in terms of the IO type, it is has more notion to come, for one is so called notion of sequence, becasue we are taking two I/O actions and we're sequencing, or gluing, them into one. We have to extract the function from the first I/O action, but to extract a result from an I/O action, it has to be performed.
suppose we are writing a function like below.
myAction :: IO String
myAction = do
a <- getLine
b <- getLine
return $ a ++ b
another way is writing as follow.
myAction :: IO String
myAction = (++) <$> getLine <*> getLine
if we regress to the box analogy, we can image getLine as a box that will go out into the real world and fetch us a string. Doing (++) <$> getLine <*> getLine makes a new, bigger box that sends those two boxes out to fetch lines from the terminal and then presents the concatenation of those two lines as its result.
another instance of Applicate is the (->) , do you still remember?
the real Applicative implementation for the
instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
let's see some exapmle of Appicate on the -> r
ghci> (pure 3) "blah"
3
this makes it even more hard to understand
ghci> pure 3 "blah"
3
a more real and obscure example is like this
ghci> :t (+) <$> (+3) <*> (*100)
(+) <$> (+3) <*> (*100) :: (Num a) => a -> a
ghci> (+) <$> (+3) <*> (*100) $ 5
508
If you want to have some one-2-one association from the functor and the argument that it shall applies to (some of the zipWith functions, you can try this)
instance Applicative ZipList where
pure x = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWith (\f x -> f x) fs xs)
and you can use the getZipList to get a Show instance of ZipList, here is some of hte ZipList function in dry run.
ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100,100]
[101,102,103]
ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100..]
[101,102,103]
ghci> getZipList $ max <$> ZipList [1,2,3,4,5,3] <*> ZipList [5,3,1,2]
[5,3,3,4]
ghci> getZipList $ (,,) <$> ZipList "dog" <*> ZipList "cat" <*> ZipList "rat"
[('d','c','r'),('o','a','a'),('g','t','t')]
there is also an liftA2 function defined in the Control.Applicative module. the type of the liftA2 is liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
what it does is to lift a functio which takes two parameter (like most binary operator does) . so, it takes a normal binary function and promotes it to a function that operates on two functors.
e.g of the use of liftA2 functor.
ghci> liftA2 (:) (Just 3) (Just [4])
Just [3,4]
ghci> (:) <$> Just 3 <*> Just [4]
Just [3,4]
and to generalize something from the above, we can define some function is called sequenceA, and the definition is like this:
-- file
-- applicative_sequence.hs
-- description:
-- implement a sequence applicative
import Control.Applicative
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA [] = pure []
sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
-- no surprise, this is a recursive implementation of sequenceA
-- another way to implement this to use the foldr mehod
sequenceA' :: (Applicative f) => [f a] -> f[a]
sequenceA' [] = pure []
sequenceA' = foldr (liftA2 (:)) (pure [])
we can have some unexpected combination with the sequenceA impl, here are some
ghci> map (\f -> f 7) [(>4),(<10),odd]
[True,True,True]
ghci> and $ map (\f -> f 7) [(>4),(<10),odd]
True
-- replaced with the sequenceA methods
ghci> sequenceA [(>4),(<10),odd] 7
[True,True,True]
ghci> and $ sequenceA [(>4),(<10),odd] 7
True
with sequenceA to simulate the method call of the list comprehension .
ghci> sequenceA [[1,2,3],[4,5,6]]
[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
ghci> [[x,y] | x <- [1,2,3], y <- [4,5,6]]
[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
and sequenceA on the IO actions
ghci> sequenceA [getLine, getLine, getLine]
heyh
ho
woo
["heyh","ho","woo"]
分享到:
相关推荐
Atom-haskell-ghc-mod.zip,haskell-ghc-mod atom packagehaskell ghc mod atom包,atom是一个用web技术构建的开源文本编辑器。
Atom-ide-haskell-hoogle.zip,在光标下显示符号的滚动信息艾德·哈斯克尔·胡格尔,atom是一个用web技术构建的开源文本编辑器。
haskell-mode emacs haskell-mode emacs
Haskell-Data-Analysis-Cookbook, Haskell数据分析 cookbook的附带源代码 Haskell-Data-Analysis-Cookbook这是 Haskell数据分析 cookbook的附带源代码。最新的源代码可以在GitHub上获得: ...
从1.0.0开始,haskell-ghc-mod提供haskell-completion-backend服务。 注意:在1.0.0之前,提供了ide-backend服务。 它已被废弃以支持ide-haskell的UPI。 您可以在找到描述 执照 版权所有:copyright:2015 Atom-...
Programming-in-Haskell-2nd-Edition.pdf
haskell-ghc-mod原子包 该软件包主要用作后端。 Haskell ghc-mod打开通往ghc-modi的管道,并查询类型,信息并检查错误。 安装与配置 请参考官方文档站点 服务中心API 从1.0.0版本开始,haskell-ghc-mod提供...
用于 haskell-relational-record 的 MySQL 驱动程序 这个项目被合并到 。 准备 $ git clone git@github.com:khibino/haskell-relational-record.git $ git clone git@github.com:bos/hdbc-mysql.git $ git clone ...
Get Programming with HASKELL-2018-英文版
演示医疗用例的参考DAML应用程序 -Haskell-TypeScript-下载
haskell-chart, haskell的2D 图表库 图 haskell的2D 图表库进一步的信息可以在关联的 wiki中找到。
Server Metaprogramming Ruby-Pyton-Groovy-Haskell-Erlang.pdf
A History of Haskell - Being Lazy With Class
haskell-lsp-client 该软件包适用于希望使其文本编辑器与兼容的文本编辑器的开发人员。 我已经开发了此软件包,并计划将其集成到。 示例客户端 该存储库中包含一个示例客户端。 此示例客户端仅运行并打开在命令行...
Atom-ide-haskell-cabal.zip,Cabal backend provider for ide-haskellIDE Haskell Cabal套餐,atom是一个用web技术构建的开源文本编辑器。
你可以在找到 haskell-brainfuck用法图书馆 import HaskBF.Evalimport qualified Data.ByteString.Lazy as BSimport Control.Monad.Statemain = do -- The following will evaluate the file using stdin and ...
Atom-atom-haskell-scry.zip,De-emphasize qualified Haskell identifiers.SCRY,atom是一个用web技术构建的开源文本编辑器。
Atom-atom-haskell-pointfree.zip,atom包:将选择转换为无点或有点表示Haskell无点包,atom是一个用web技术构建的开源文本编辑器。
Haskell - The Craft of Functional Programming, 2ed (Addison-Wesley, 1999) by Tantanoid 已加书签
Atom-haskell-debug.zip,使用ghci在atom中实现图形haskell调试器Haskell调试,atom是一个用web技术构建的开源文本编辑器。