`

haskell - Functors, Applicative Functors and Monoids

阅读更多

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 

 

分享到:
评论

相关推荐

    Haskell趣学指南---文字版.pdf

    第十一章讨论了Functors、Applicative Functors、Monoids,它们是构建复杂函数式程序的基础。通过理解Monoids和其在数据结构折叠(Folding)中的应用,读者可以学习到如何将数据结构聚合成单一值。 第十二章开始,...

    B10-Haskell趣学指南.pdf

    本章重温了Functor的概念,并介绍了Applicative Functors和Monoids,这些都是Haskell中处理数据结构和组合函数的强大工具。通过使用Monoids,我们能高效地折叠(fold)数据结构。 ### 第十二章与第十三章:来看看几...

    Haskell趣学指南

    #### 十一、Functors, Applicative Functors与Monoids - **Functors**:介绍Functor类型类及其应用。 - **Applicative Functors**:讲解Applicative Functor类型类及其在函数式编程中的作用。 - **Monoids**:学习...

    LtuPatternFactory:最终的Lambda模式工厂:FP,Haskell,Typeclassopedia与软件设计模式

    5. **Monoids**:Monoids是一类具有结合性和单位元的数据结构,例如整数加法或字符串连接。它们在FP中广泛用于聚合和组合操作。 6. **Monad Transformers**:Monad Transformer允许我们将不同类型的Monad组合在一起...

Global site tag (gtag.js) - Google Analytics