Functor is the Functors , or functors are essentially haskell. We will introduce something about functors, then we might progress to Applicative Functors.
Newtype keyword and others.
So, the first question, what is a Functor, a functor is a typeclass which has the fmap function as below. here is what you might expect for a Functor definition .
class Functor where fmap :: (a -> b) -> f a -> f b
so it has the only method which is called "fmap", and it is able to take a fucntion which return a and return b, and a box with a in it, it can return a box with b in it.
A note on the instance of Applicative, when are dealing with some box types, such as either, which takes two type parameter, we cannot do something like this
instance Functor Either where
it is because the Functor has the following kind (* -> *);
so , you have to partially apply first to the either type, so you can do this;
instance Functor Either a where
and the function type that it has is
fmap :: (b -> c) -> Either a b -> Either a c
the IO is also an instance of Functor, so the definition of the IO instance of Functor is like this:
instance Functor IO where fmap f action = do result <- action return (f result)
so you can do fmap on the IO actions, so instead of writing the following.
main = do line <- getLine let line' = reverse line putStrLn $ "You said " ++ line' ++ " backwards!" putStrLn $ "Yes, you really said" ++ line' ++ " backwards!"
and then you can produce the following.
main = do line <- fmap reverse getLine putStrLn $ "You said " ++ line ++ " backwards!" putStrLn $ "Yes, you really said" ++ line ++ " backwards!"
a common pattern to use the Fmap with lambad or with function composition instead of binding to variable.s, here is an example of the use of function composition .
import Data.Char import Data.List main = do line <- fmap (intersperse '-' . reverse . map toUpper) getLine putStrLn line
or you can use the following lambda expression .
import Data.Char import Data.List main = do line <- fmap (\xs -> intersperse '-' (reverse (map toUpper xs))) getLine putStrLn line
Now, we have flex our muscles on the functors, now let's see a very special form of functor the applicativfe functor, before we introduce to the Applicative functor, let's first try to see something as below.
instance Functor ((->) r) where fmap f g = (\x -> f (g x))
what does this mean, ((->) r) what is that ??
Let's do some transformatio, first, we know that we can write r -> a as (->) r a , so we can see (->) in a sllightly different light, because we see that it is just a type consturctor which takes two two type parameter, however, as we have already discussed that Functor only take one type parametr, but forturnately, we can partially apply the (->) method, so we have (->) r...
so let's suppose we will make the (->) r part of the Functor .. so this is how we will implement it .
instance Functor ((->) r) where fmap f g = (\x -> f (g x))
and if the syntax allow for it, we can writ it as follow (which infers that it is not allowed!!!)
instance Functor (r ->) where fmap f g = (\x -> f (g x))
and because applicative functor will reminds us of the function composition, we can write the fmap as the following. (how neat, how simple it is )
instance Functor ((->) r) where fmap = (.)
now, let's see how we can leverage the applicative functor.
ghci> :t fmap (*3) (+100) fmap (*3) (+100) :: (Num a) => a -> a ghci> fmap (*3) (+100) 1 303 ghci> (*3) `fmap` (+100) $ 1 303 ghci> (*3) . (+100) $ 1 303 ghci> fmap (show . (*3)) (*100) 1 "300"
let's review how to interpret the Applicative functor, suppose that we can substitue the f with ((->) r); and we know that the type of fmap is
(a -> b) -> f a -> f b
and we replace the f with the ((->) r) then we havfe
(a -> b) -> ((->r ) a) -> ((->r) b )
and we know that
((->r) a)
is the same as
(r -> a)
there are some other things that we might have overlooked, first we said that the type of fmap is fmap:: (a -> b ) -> f a -> f b ; while the class constraint is missing here : (Functor f) =>
Also, if we reorganize the fmap as follow fmap:: (a -> b ) -> (f a -> f b), we know that it takes a functor and return a new functor, and we calls this lifting function. you can check this by the following.
ghci> :t fmap (*2) fmap (*2) :: (Num a, Functor f) => f a -> f a ghci> :t fmap (replicate 3) fmap (replicate 3) :: (Functor f) => f a -> f [a]
here we comes to the functor rules, the functor rules dictate the following.
The first functor law states that if we map the id function over a functor, the functor that we get back should be the same as the original functor.
If we write that a bit more formally, it means that fmap id = id. So essentially, this says that if we do fmap id over a functor, it should be the same as just calling id on the functor. Remember, id is the identity function, which just returns its parameter unmodified. It can also be written as \x -> x. If we view the functor as something that can be mapped over, the fmap id = id law seems kind of trivial or obvious.
The second law says that composing two functions and then mapping the resulting function over a functor should be the same as first mapping one function over the functor and then mapping the other one. Formally written, that means thatfmap (f . g) = fmap f . fmap g. Or to write it in another way, for any functor F, the following should hold:fmap (f . g) F = fmap f (fmap g F).
we will then see an example of instance of Functor, but do not obey the functor rules, why this is important, it is important because if the type obeys the rule, we can make some assumption how the type act..
-- file -- functor_counter_example.hs -- description: -- data CMaybe a = CNothing | CJust Int a deriving (Show) instance Functor CMaybe where fmap f CNothing = CNothing fmap f (CJust counter x) = CJust (counter+1) (f x) -- and if you run this function -- *Main> fmap id (CJust 0 "haha") -- CJust 1 "haha" -- it does not obey the rule of law 1, the fmap id f == fmap f
相关推荐
第十一章讨论了Functors、Applicative Functors、Monoids,它们是构建复杂函数式程序的基础。通过理解Monoids和其在数据结构折叠(Folding)中的应用,读者可以学习到如何将数据结构聚合成单一值。 第十二章开始,...
本章重温了Functor的概念,并介绍了Applicative Functors和Monoids,这些都是Haskell中处理数据结构和组合函数的强大工具。通过使用Monoids,我们能高效地折叠(fold)数据结构。 ### 第十二章与第十三章:来看看几...
#### 十一、Functors, Applicative Functors与Monoids - **Functors**:介绍Functor类型类及其应用。 - **Applicative Functors**:讲解Applicative Functor类型类及其在函数式编程中的作用。 - **Monoids**:学习...
5. **Monoids**:Monoids是一类具有结合性和单位元的数据结构,例如整数加法或字符串连接。它们在FP中广泛用于聚合和组合操作。 6. **Monad Transformers**:Monad Transformer允许我们将不同类型的Monad组合在一起...