首页 > 代码库 > 怎样理解Functor与Monad

怎样理解Functor与Monad

1. 复合函数操作符

Prelude> :t (.)(.) :: (b -> c) -> (a -> b) -> a -> cPrelude> (.) ((+) 5) ((*) 2) 413

  

所以(.)操作符的作用,是将4作为参数传递给((*) 2)函数,再将结果传递给((+) 5)函数。

这就是数学里面的复合函数:

f(x) = 2x

g(x) = x + 5

g(f(x)) = g(2x) = (2x) + 5 = 2x + 5

g(f(4)) = 2*4 + 5 = 13

 

 2. Functor

194 {- | The ‘Functor‘ class is used for types that can be mapped over.195 Instances of ‘Functor‘ should satisfy the following laws:196 197 > fmap id  ==  id198 > fmap (f . g)  ==  fmap f . fmap g199 200 The instances of ‘Functor‘ for lists, ‘Data.Maybe.Maybe‘ and ‘System.IO.IO‘201 satisfy these laws.202 -}203 204 class  Functor f  where205     fmap        :: (a -> b) -> f a -> f b206 207     -- | Replace all locations in the input with the same value.208     -- The default definition is @‘fmap‘ . ‘const‘@, but this may be209     -- overridden with a more efficient version.210     (<$)        :: a -> f b -> f a211     (<$)        =  fmap . const

  id是一个函数

Prelude> :t idid :: a -> aPrelude> id "Daniel""Daniel"Prelude> id 11Prelude> id TrueTruePrelude> id Just "Happy"Just "Happy"Prelude> id NothingNothing

  Functor是一种typeclass,用来定义一种允许的操作集合,这里是fmap,并且对于fmap提出了需要满足的条件:

  • fmap id == id
  • fmap (f . g) == fmap f . fmap g

可以视为"交换律"和“分配律”

Prelude Data.Char> fmap isDigit ((++) [‘0‘..‘9‘] [‘a‘..‘f‘])[True,True,True,True,True,True,True,True,True,True,False,False,False,False,False,False]

  

Prelude Data.Char> :t isDigitisDigit :: Char -> Bool

  

fmap不仅可以操作List(此时与List的函数map作用相同),还可以操作比如Maybe

Prelude Data.Char> fmap isDigit (Just ‘a‘)Just FalsePrelude Data.Char> fmap isDigit (Nothing)NothingPrelude Data.Char> fmap isDigit (Just ‘1‘)Just True

  

3. Manod

Monad与Functor一样,也是用来定义类型可以进行的操作集合。

Prelude Data.Char> :i Monadclass Monad m where  (>>=) :: m a -> (a -> m b) -> m b  (>>) :: m a -> m b -> m b  return :: a -> m a  fail :: String -> m a  	-- Defined in `GHC.Base‘instance Monad Maybe -- Defined in `Data.Maybe‘instance Monad (Either e) -- Defined in `Data.Either‘instance Monad [] -- Defined in `GHC.Base‘instance Monad IO -- Defined in `GHC.Base‘instance Monad ((->) r) -- Defined in `GHC.Base‘

  

可以看到,这些操作包括(>>=) (>>) return fail

(>>)执行两个操作,但是放弃前一个操作的结果

Prelude Data.Char> :t (>>)(>>) :: Monad m => m a -> m b -> m bPrelude Data.Char> (>>) "Daniel" "King""KingKingKingKingKingKing"

  

212 213 {- | The ‘Monad‘ class defines the basic operations over a /monad/,214 a concept from a branch of mathematics known as /category theory/.215 From the perspective of a Haskell programmer, however, it is best to216 think of a monad as an /abstract datatype/ of actions.217 Haskell‘s @do@ expressions provide a convenient syntax for writing218 monadic expressions.219 220 Minimal complete definition: ‘>>=‘ and ‘return‘.221 222 Instances of ‘Monad‘ should satisfy the following laws:223 224 > return a >>= k  ==  k a225 > m >>= return  ==  m226 > m >>= (\x -> k x >>= h)  ==  (m >>= k) >>= h227 228 Instances of both ‘Monad‘ and ‘Functor‘ should additionally satisfy the law:229 230 > fmap f xs  ==  xs >>= return . f231 232 The instances of ‘Monad‘ for lists, ‘Data.Maybe.Maybe‘ and ‘System.IO.IO‘233 defined in the "Prelude" satisfy these laws.234 -}235 236 class  Monad m  where237     -- | Sequentially compose two actions, passing any value produced238     -- by the first as an argument to the second.239     (>>=)       :: forall a b. m a -> (a -> m b) -> m b240     -- | Sequentially compose two actions, discarding any value produced241     -- by the first, like sequencing operators (such as the semicolon)242     -- in imperative languages.243     (>>)        :: forall a b. m a -> m b -> m b244         -- Explicit for-alls so that we know what order to245         -- give type arguments when desugaring246 247     -- | Inject a value into the monadic type.248     return      :: a -> m a249     -- | Fail with a message.  This operation is not part of the250     -- mathematical definition of a monad, but is invoked on pattern-match251     -- failure in a @do@ expression.252     fail        :: String -> m a253 254     {-# INLINE (>>) #-}255     m >> k      = m >>= \_ -> k256     fail s      = error s

  

(>>=)是将前一个操作的结果作为参数传递给后一个操作,但是注意需要使用return将从a到b的正常转换(a -> b)变成(a -> mb),即(a -> ma)(a - b) = (a -> mb)

Prelude Data.Char> :t returnreturn :: Monad m => a -> m aPrelude Data.Char> :t (>>=)(>>=) :: Monad m => m a -> (a -> m b) -> m bPrelude Data.Char> (>>=) (return ((++) "Daniel" "King")) ((:) ‘X‘)"XDanielKing"

  

Prelude Data.Char> (>>=) (return ((++) "Daniel" "King")) ((++) "Hello ")"Hello DanielKing"

  

所以(>>=)和return是配合使用的,效果很类似于Unix下的管道操作。

Monad的设计想法是允许pure的Haskell可以产生一些side effect,或者说除了自身的值以外,可以保存下一些状态信息。

比如在这里, ((++) "Daniel" "King")的结果就传递给了后面的action,这样就可以用来更新状态信息。

 

比较明显的应用是在IO以及Exception Handling上面。