Source Address: Haskell Learning-monad
What is Monad
Haskell is a purely functional language, the advantage of pure function is safe and reliable. The function output is entirely dependent on the input, there is no implicit dependency, and its existence is as perfect as a mathematical formula. But the pure function is isolated from the external environment, even the most basic input and output can not be completed. and Monad is a solution given by Haskell. But Monad is not just an abstraction of IO operations, it is a common abstraction between many similar operations. So Monad solves the problem not confined to IO, like Haskell in maybe and [] are Monad. The beautiful error handling in Haskell, the do notation and the flexible listcomprehensionare all Monad 's contributions.
Monad is basically a reinforced version of the applicative Functor, just as applicative Functor is the Functor of the enhanced version. So on the basis of fully understanding applicative Functor , the transition to Monad is actually very smooth.
-- Monad的定义class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b x >> y = x >>= \_ -> y fail :: String -> m a fail msg = error msg
- return is completely different from the return in other languages, which is a function that packs ordinary values into a context and is not a keyword that ends the function execution. is actually equivalent to purein applicative.
- >> ignores the return value of the preceding expression and executes the current expression directly.
- >>= accepts a monadic value (that is, has a context, can be likened to a box with a normal value) and feeds it to a function that accepts a normal value, and callbacks a monadic value.
- The =<< is the same as the above >>= function, just in the reverse order.
The principle of Monad
To collaborate between functions, they must be interconnected in various forms. But how do you isolate pure and side-effect functions while allowing two kinds of functions to reuse each other?
As an example of IO operation, in order to fully isolate the pure function and IO functions, it is not possible to implement IO char -a char in Haskell such that an input is an IO type return value but is a normal type of function. Otherwise, the side effect function can easily become a pure function. In fact, once the parameters have Io, the return value must have IO, which guarantees full isolation.
So how do you get pure functions and IO functions to reuse each other? This depends on the return and >>= functions defined in IO Monad. return (not a keyword in Haskell, just a function name) is the function of raising (boxing) a value of a type a to a value of type IO a Char-I O Char . With this function, a pure function can be returned to a function with a return value of IO with side effects.
With ascension without descent operation, how to compound Putchar:: char-io () and GetChar:: Io char . GetChar reads a character from IO and Putchar writes the character to Io. But GetChar returns the IO char type, and Putchar needs a normal char type, what if it doesn't match? The >>= function! The type of >>= is
IO a -> (a -> IO b) -> IO b
This allows the >>= to connect getChar and putchar and write input to the output.
getChar >>= putChar
You can see that the >>= operation is actually a type descent (or unboxing) operation, and the function return value that performs the descent operation must also be the IO type. This allows both pure and side-effect functions to be fully isolated, and functions can be reused with each other. Through return and >>= two parallel Worlds (categories) There is a controllable AC channel.
Do notation
The do representation of Haskell is actually Monad's syntactic sugar: it provides us with a way to write monadic code without using (>>=) and anonymous functions. The process of removing do syntax sugar is to convert it to (>>=) and anonymous functions.
The do notation can use semicolons , and curly braces {} block statements, but generally use one line of expressions, and different scopes are distinguished by different indentation.
We also take the IO as an example, accept two keyboard input, and then combine two input strings into a single string, the last screen printout. The >>= accepts the value of the preceding expression,>> ignores the value of the preceding expression, which is used here to return the IO String that is actually returned. Because Haskell is derived from the automatic type deduction. The expression code for Monadic is as follows:
(++) <$> getLine <*> getLine >>= print >> return "over"111222> "111222"> "over"
Using do rewriting is clearly clearer and almost as familiar as the command-style language.
<- indicates that the normal value is removed from the monadic value and can be seen as the desired value for unpacking the box.
foo :: IO String foo = do x <- getLine y <- getLine print (x ++ y) return "over"
Do syntax correspondence mode
do {e} -> edo {e; es} -> e >> do {es}do {let decls; es} -> let decls in do {es}do {p <- e; es} -> e >>= \p -> es
Monad type
Take a look at a few of the default Monad types, all of which must implement the return,>>=,fail functions.
Maybe
Any step in the middle as long as there is nothing, The result will be returned in advance. The Just value is returned without any unexpected conditions.
-- Maybe 的 Monad instanceinstance Monad Maybe where return x = Just x Nothing >>= f = Nothing Just x >>= f = f x fail _ = Nothing-- 实例Just 3 >>= (\x -> Nothing >>= (\y -> Just (show x ++ y)))> NothingJust 3 >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y)))> Just "3!"
Use the do notation to write this:
foo :: Maybe Stringfoo = do x <- Just 3 y <- Just "!" Just (show x ++ y)
List
>>= basically accepts a value that has a context, feeds him into a function that accepts only ordinary values, and callbacks a value that has a context. [] is actually equivalent to nothing.
When we feed a list to this function using >>= , lambda maps each element, computes a list of lists that contain a list of lists, and then flattening the list to get a list of layers. This is the process by which we get the list to process Mondic value.
--list 的 Monad instanceinstance Monad [] where return x = [x] xs >>= f = concat (map f xs) fail _ = []-- 实例[3,4,5] >>= \x -> [x,-x]> [3,-3,4,-4,5,-5][1,2,3] >>= \x -> return (-x)> [-1,-2,-3]
List Comprehension is just Monad 's grammatical sugar.
[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch) -- Monad[ (n,ch) | n <- [1,2], ch <- ['a','b'] ] -- list comprehension> [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
The filter for list comprehension is basically consistent with the guard.
[1..50] >>= (\x -> guard ('7' `elem` show x) >> return x)> [7,17,27,37,47]
With do rewrite, if you do not write the last line of return x, then the entire list will be a list containing a heap of empty tuples .
sevensOnly :: [Int]sevensOnly = do x <- [1..50] guard ('7' `elem` show x) return x-- 对应的 list comprehension[ x | x <- [1..50], '7' `elem` show x ]> [7,17,27,37,47]
Either
In the Control.Monad.Error there is the Error Monad instance.
instance (Error e) => Monad (Either e) where return x = Right x Right x >>= f = f x Left err >>= f = Left err fail msg = Left (strMsg msg)Right 3 >>= \x -> return (x + 100) :: Either String Int> Right 103
Monad rules
Return a >>= F = = f A
= = The expression on the left is equivalent to the expression on the right. If we just wrap a value in Monad and then use the (>>=) Call, we don't need to use return ; This rule has practical implications for our code style: We shouldn't write unnecessary code. This rule guarantees that a short notation and redundancy are equivalent.
return 3 >>= (\x -> Just (x+100000)) -- 和直接函数调用没有区别
M >>= return = = m
This rule is also good for style: If in a series of action blocks, if the last sentence is the correct result that needs to be returned, then return is not required, and as with the first rule, this rule can help us simplify the code.
Just "move on up" >>= return -- 可以不需要 return
(M >>= f) >>= g = = M >>= (\x, F x >>= g)
When we use >>= to string together a string of monadic function, their sequencing should not affect the results.
And that's not the law of the Union? We can extract the child action to combine it into a new action.
(<=<) can be used to synthesize two monadic functions, similar to the normal function (.), while (>=>) indicates the reverse of the binding order.
(<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c)f <=< g = (\x -> g x >>= f)-- 普通函数结合(.)let f = (+1) . (*100)f 4> 401-- 合成monadic functions (<=<)let g = (\x -> return (x+1)) <=< (\x -> return (x*100))Just 4 >>= g> Just 401-- 也可以将 monadic 函数用foldr,id 和(.)合成 let f = foldr (.) id [(+1),(*100),(+1)]f 1> 201
Monad (-) R morphology
R is not just a functor and applicative functor, but also a monad.
Every monad is a applicative functor, and every applicative functor is a functor. Although Moand have the nature of functor and applicative functor , they do not necessarily have functor and applicative Instance definition.
instance Monad ((->) r) where return x = \_ -> x h >>= f = \w -> f (h w) w
Monad Auxiliary functions
The underlined function is equivalent to a function that is not underlined, but does not return a value
>>= :: m a -> (a -> m b) -> m b=<< :: (a -> m b) -> m a -> m bform :: t a -> (a -> m b) -> m (t b)form_ :: t a -> (a -> m b) -> m ()mapM :: (a -> m b) -> t a -> m (t b)mapM_ :: (a -> m b) -> t a -> m ()filterM :: (a -> m Bool) -> [a] -> m [a]foldM :: (b -> a -> m b) -> b -> t a -> m bsequence :: t (m a) -> m (t a)sequence_ :: t (m a) -> m ()liftM :: (a1 -> r) -> m a1 -> m rwhen :: Bool -> f () -> f ()join :: m (m a) -> m a
Some of the functions that are often used in IO
-
sequence
sequence accepts a string of I/O actions and callbacks one that executes their I/O action sequentially. The result of the operation is the result of a sequence of I/O actions wrapped in an I/O action.
main = do a <-getLine b <-getLine c <-getLine Print [a,b,c]
/pre> Can actually be written as
main = do rs <-sequence [GetLine, GetLine, GetLine] Print Rs
A common use of
is to map functions such as print or putstrln to a string.
sequence (map print [1,2,3,4,5]) 12345[(), (), (), (), ()]
mapm and mapm_
Because the function of a callback I/O action on a string map, and then sequence This action is too common. So mapm and mapm_are available in the function library. MAPM accepts a function followed by a string column, and then sequence the result with a function map for the string. mapm_ did the same thing, but he threw away the results of the operation. mapm_ is most commonly used in cases where we do not care about the I/O action results.
mapM print [1,2,3]123[(),(),()]mapM_ print [1,2,3]123
form and form_ are similar to mapm and mapm_ , but only advance the list parameters.
Others are defined in Monad and are equivalent to functions that are in functor or applicative functor .
Liftm
LIFTM and fmap equivalent, there are liftM3,liftM4 and liftM5
liftM :: (Monad m) => (a -> b) -> m a -> m bliftM f m = m >>= (\x -> return (f x))liftM (*2) [1,2]> [2,4]
Ap
The AP is basically <*>, but he is confined to Monad rather than applicative.
ap :: (Monad m) => m (a -> b) -> m a -> m bap mf m = do f <- mf x <- m return (f x)ap [(*2)] [1,2,3]> [2,4,6]
Join
m >>= F is always equivalent to join (Fmap f m) This property is very useful
join :: (Monad m) => m (m a) -> m ajoin (Just (Just 9))> Just 9join [[1,2,3],[4,5,6]] -- 对于 list 而言 join 不过就是 concat> [1,2,3,4,5,6]
Filterm
Filterm, in addition to being able to do the filter action, but also to maintain the monadic context.
filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]filterM (\x -> return (x > 2)) [1,2,3,4]> [3,4]
Foldm
FOLDL 's monadic version is called FOLDM .
foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m afoldM (\x y -> return (x + y)) 0 [1,2,3]> 6