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