怎样理解Functor与Monad
1. 复合函数操作符
Prelude> :t (.) (.) :: (b -> c) -> (a -> b) -> a -> c Prelude> (.) ((+) 5) ((*) 2) 4 13
所以(.)操作符的作用,是将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 == id 198 > fmap (f . g) == fmap f . fmap g 199 200 The instances of 'Functor' for lists, 'Data.Maybe.Maybe' and 'System.IO.IO' 201 satisfy these laws. 202 -} 203 204 class Functor f where 205 fmap :: (a -> b) -> f a -> f b 206 207 -- | Replace all locations in the input with the same value. 208 -- The default definition is @'fmap' . 'const'@, but this may be 209 -- overridden with a more efficient version. 210 (<$) :: a -> f b -> f a 211 (<$) = fmap . const
id是一个函数
Prelude> :t id id :: a -> a Prelude> id "Daniel" "Daniel" Prelude> id 1 1 Prelude> id True True Prelude> id Just "Happy" Just "Happy" Prelude> id Nothing Nothing
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 isDigit isDigit :: Char -> Bool
fmap不仅可以操作List(此时与List的函数map作用相同),还可以操作比如Maybe
Prelude Data.Char> fmap isDigit (Just 'a') Just False Prelude Data.Char> fmap isDigit (Nothing) Nothing Prelude Data.Char> fmap isDigit (Just '1') Just True
3. Manod
Monad与Functor一样,也是用来定义类型可以进行的操作集合。
Prelude Data.Char> :i Monad class 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 b Prelude 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 to 216 think of a monad as an /abstract datatype/ of actions. 217 Haskell's @do@ expressions provide a convenient syntax for writing 218 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 a 225 > m >>= return == m 226 > m >>= (\x -> k x >>= h) == (m >>= k) >>= h 227 228 Instances of both 'Monad' and 'Functor' should additionally satisfy the law: 229 230 > fmap f xs == xs >>= return . f 231 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 where 237 -- | Sequentially compose two actions, passing any value produced 238 -- by the first as an argument to the second. 239 (>>=) :: forall a b. m a -> (a -> m b) -> m b 240 -- | Sequentially compose two actions, discarding any value produced 241 -- by the first, like sequencing operators (such as the semicolon) 242 -- in imperative languages. 243 (>>) :: forall a b. m a -> m b -> m b 244 -- Explicit for-alls so that we know what order to 245 -- give type arguments when desugaring 246 247 -- | Inject a value into the monadic type. 248 return :: a -> m a 249 -- | Fail with a message. This operation is not part of the 250 -- mathematical definition of a monad, but is invoked on pattern-match 251 -- failure in a @do@ expression. 252 fail :: String -> m a 253 254 {-# INLINE (>>) #-} 255 m >> k = m >>= \_ -> k 256 fail s = error s
(>>=)是将前一个操作的结果作为参数传递给后一个操作,但是注意需要使用return将从a到b的正常转换(a -> b)变成(a -> mb),即(a -> ma)(a - b) = (a -> mb)
Prelude Data.Char> :t return return :: Monad m => a -> m a Prelude Data.Char> :t (>>=) (>>=) :: Monad m => m a -> (a -> m b) -> m b Prelude 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上面。
如果参考数学中的概念,Monad可以被看作是虚数体系,或者是另外一个维度的类型;
普通的类型与Monad类型之间需要显式地进行转换,
return : 普通类型转换成Monad类型
<-:Monad类型转换成普通类型
IO action都是操作的Monad类型。
*RecursiveContents Control.Monad Data.Char System.FilePath> :t forM forM :: Monad m => [a] -> (a -> m b) -> m [b] *RecursiveContents Control.Monad Data.Char System.FilePath> forM "Daniel King" $ \ch -> do return (toUpper ch) "DANIEL KING"