查看“︁Haskell/理解 Monad”︁的源代码
←
Haskell/理解 Monad
跳转到导航
跳转到搜索
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
{{信息|1=本頁正值覆寫,倘要翻閱前版,則請移步 [http://en.wikibooks.org/w/index.php?title=Haskell/Understanding_monads&oldid=933545 斯處]。}} {{警告|如有所不明,請先參閱[http://homepages.inf.ed.ac.uk/wadler/papers/constraints/constraints.pdf 《Monad for Functional Programming》],而後再作譯閱。建议毋須直译英文版,而僅須依照[http://homepages.inf.ed.ac.uk/wadler/topics/monads.html Philip Wadler的文章]扩充下文。如有任何意見,请至讨论頁發表。請統一翻譯專有名詞。 }} {{Haskell微型目录|chapter=Monads}} == Notes and TODOs == <i>Loose ends:</i> <ul> <li><i> Explain monadic parser combinators! But in another chapter. </i></li><li><i> The basic triad "rock, scissors, paper" err, "reader, writer, state" is best introduced in another chapter, maybe entitled "A Zoo of Monads". Reader must include the famous function <code>[(a->b)] -> a -> [b]</code> also known as <code>sequence</code>! </i></li><li><i> The <code>Random a</code> is too cute to not elaborate it further to probabilistic functional programming. The Monty Hall problem can be solved with that. Key: the implementation can be changed, it effectively becomes the Set-Monad then. Extension: <code>guard</code> and backtracking. </i></li><li><i> Exceptions are a nice way to explain monads, too. See also [http://programming.reddit.com/info/64th1/comments/c02u9mb]. </i></li> </ul> == 介绍 == === 什么是Monad === 定义:Monad是一个三元组<math>(M,\mathit{return},\gg\!\!=)</math> 由类型构造器 <math>M</math>和两个多态函数 <!--Definition: a '''monad''' is a triple <math>(M,\mathit{return},\gg\!\!=)</math> consisting of a type constructor <math>M</math> and two polymorphic functions--> <center><math>\begin{array}{lcl}\mathit{return} &::& a \to M\,a \\ (\mathit{\gg\!\!=}) &::& M\,a \to (a \to M\,b) \to M\,b\end{array}</math></center> 组成。两个函数符合下列三个定律: <!--that obey the following three laws--> <center> {| |- | ''右单元'' | <math>m \gg\!\!= \mathit{return} = m </math> |- | ''左单元'' | <math>\mathit{return}\,x \gg\!\!= f = f\,x</math> |- | ''结合律'' | <math> (m \gg\!\!= f) \gg\!\!= g = m \gg\!\!= (\lambda x.\ f\,x \gg\!\!= g)</math> |} </center> 操作符<math>\gg\!\!=</math>通常叫做"'''bind'''"。经常地,类型构造器<math>M</math>也称作monad。 <!--The operator <math>\gg\!\!=</math> is commonly called "'''bind'''". Often, one simply refers to the type constructor <math>M</math> as the monad.--> 换句话说,一个monad就是一种类似队列或者有限映射的抽象数据类型,所包含的两个操作"bind"和"return"要满足三个特性。一个特别需要注意的地方是一个monad <math>M</math>不是一个类型,而是一个"类型构造器",类似于一个从类型到类型的函数。并且,monad是一个非常抽像的概念,正如我们要看到的,"bind" 可能有很多不同的含义。 <!--In other words, a monad is a kind of abstract data type like a queue or a finite map with two operations ''bind'' and ''return'' that are supposed to fulfill three properties. One peculiarity is that a monad <math>M</math> is not a type, but a ''type constructor'', i.e. more like a function from types to types. Also, monad is a rather abstract concept; as we will see, many different meanings of ''bind'' are possible.--> 在Haskell里,我们可以将其定义为一个类型类 <!--In Haskell, we can capture this definition as a type class--> class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b 所有的<code>Monad</code>都假定满足上面的三个定律。这个类是Haskell {{Haskell库|Prelude}}的一部分,是在标准库模块{{Haskell库|Control|Monad}}里面定义的,并有一些扩展。 <!--Any instance of <code>Monad</code> is assumed to fulfill the three laws stated above. This class is, slightly expanded, part of the Haskell {{Haskell库|Prelude}} and defined in the standard library module {{Haskell库|Control|Monad}}.--> === What use are Monads? === After maturing in the mathematical discipline of [[Haskell/Category theory|category theory]] for some time, monads were introduced to programming when Eugenio Moggi showed<ref>{{引用日志|first=Eugenio|last=Moggi|date=1991|title=Notions of Computation and Monads|journal=Information and Computation|volume=93|issue=1}}</ref> how they can unify the description of the semantics of different programming languages. Depending on the concrete monad chosen, different semantics emerge. For instance, mutable state can be modeled by the monad <code>M a = s -> (a,s)</code>. Lists are a monad, too, and model nondeterminism and backtracking whereas the monad <code>M a = Either e a</code> models exceptions. One aim of the chapters on monads is to exemplify many of these different forms of computation. Of course, it's not our main interest to study programming language semantics, but it was Philip Wadler who noticed<ref>[[w:Philip Wadler]]. [http://citeseer.ist.psu.edu/wadler92comprehending.html Comprehending Monads]. Proceedings of the 1990 ACM Conference on LISP and Functional Programming, Nice. 1990.</ref> <ref>[[w:Philip Wadler]]. [http://citeseer.ist.psu.edu/wadler92essence.html The Essence of Functional Programming]. Conference Record of the Nineteenth Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages. 1992.</ref> that we can directly implement Moggi's monads in Haskell. This is a powerful idea since each monad is a little minilanguage specially suited for a particular task. For instance, to program a state transformer, we can use a monad to model state. To solve a logic problem, we use the list monad to transparently handle backtracking. To handle failure and exceptions easily, we have the <code>Either e</code> monad. And last but not least, there is the <code>IO</code> monad to perform input/output, something that did not seem to fit well into a purely functional language. This and subsequent chapters are to guide you to through these minilanguages and to show they can simplify and structure your daily Haskell code. But how can the rather short definition of monads given above relate all these very different forms of computation? They all share a common use pattern, namely the ability to combine two computations <math>f</math> and <math>g</math> into a compound computation <math>f \gg\!\!= g</math> by first "executing" <math>f</math> and "then" binding, i.e. feeding the result to <math>g</math>. This is what the operator <math>\gg\!\!=</math> captures and that's why it's called "bind". In other words, <math>\gg\!\!=</math> is similar to function composition. Of course, depending on the underlying monad, "executing" and "then" may have quite different meanings. Don't worry if this seems too abstract now, we will detail the genesis of <math>\gg\!\!=</math> with our first example monad in the section [[#Stateful Computations|Stateful Computations]]. == Stateful Computations == We will introduce <math>\gg\!\!=</math> with a practical example of stateful computations: random number generation. First, we will explain how a deterministic computer can generate "random" numbers. Then, we will introduce ''bind'' and ''return'' as useful functions for combining random number generators. And last but not least, the <code>IO</code> monad will be introduced intuitively as a state transformer changing the "world state". === Random Number Generation === Computers usually create random numbers by starting with a single random number (frequently called "'''seed'''") and applying some arithmetic operations to it to get a new random number. By repeating this process, we get a sequence of fairly random numbers. Of course, since each number is generated in a deterministic way from the previous one, they are not truly random, but '''''pseudo''-random numbers'''. But by choosing the arithmetic operations carefully to properly "scramble" the input number, they behave like real random numbers. To give an impression of how this "scrambling" works, here's an example function that generates a pseudo-random number from a previous one:<syntaxhighlight lang="haskell"> type Seed = Int randomNext :: Seed -> Seed randomNext rand = if newRand > 0 then newRand else newRand + 2147483647 where newRand = 16807 * lo - 2836 * hi (hi,lo) = rand `divMod` 127773 </syntaxhighlight>There is much research on constructing good [[w:Pseudo-random number generator|pseudo-random number generators]], but fortunately, the Haskell standard library module {{Haskell库|p=random|v=1.0.0.0|System|Random}} already implements a ready-to-use generator for us. However, its interface is best explained with <code>randomNext</code>, so we will stick to that for now. Let's implement a function that simulates a single roll of a die, i.e. that returns a random number from 1 to 6. But <code>randomNext</code> uses large numbers since they can be scrambled much better, so we need to convert a <code>Seed</code> to a number from 1 to 6. This can be done by dividing the <code>Seed</code> by 6 and taking the remainder<syntaxhighlight lang="haskell"> toDieRoll :: Seed -> Int toDieRoll seed = (seed `mod` 6) + 1 </syntaxhighlight>So, given an initial random <code>Seed</code>, we can roll a die with it<syntaxhighlight lang="haskell"> toDieRoll 362354 -- hand-crafted initial random seed :-) 3 </syntaxhighlight>But something is missing: what if we want to roll the die a second time? For that, we have to generate a new random <code>Seed</code> from the old one via <code>randomNext</code>. In other words, we have to change the current <code>Seed</code>, i.e. the '''state''' of our pseudo-random number generator. In Haskell, this can be accomplished by returning the new state in the result<syntaxhighlight lang="haskell"> rollDie :: Seed -> (Int, Seed) rollDie seed = ((seed `mod` 6) + 1, randomNext seed) </syntaxhighlight>This is the description of a '''state transformer''': an initial state (the <code>Seed</code>) is transformed to a new one while yielding a result (the <code>Int</code> between 1 and 6). We can visualize it as: [[Image:monad_state_random1.jpg]] To roll two dice and sum their pips, we can now feed the <code>Seed</code> from the first roll to the second roll. Of course, we have to return the new state from the second dice roll as well for our function <code>sumTwoDice</code> to be as useful as <code>rollDie</code>:<syntaxhighlight lang="haskell"> sumTwoDice :: Seed -> (Int, Seed) sumTwoDice seed0 = let (die1, seed1) = rollDie seed0 (die2, seed2) = rollDie seed1 in (die1 + die2, seed2) </syntaxhighlight>Again, a picture shows clearly how the state is passed from one <code>rollDie</code> to the next. Note that <code>randomNext</code> does not appear in the definition of <code>sumTwoDice</code>, the state change it performs is already embedded in <code>rollDie</code>. The function <code>sumTwoDice</code> merely propagates the state updates. [[Image:monad_state_random2.jpg]] This is the model that {{Haskell库|p=random|v=1.0.0.0|System|Random}} employs, so we can now elaborate on its concrete interface. The library uses two type classes: <code>RandomGen</code> and <code>Random</code>. Any instance of the former acts similar to our <code>Seed</code>, it's just called "random number generator", not "seed". This makes sense since the seed may have more complicated internals than just an <code>Int</code> and is closely linked to the function that generates new pseudo-random numbers. In any case, the module exports a convenient random number generator <code>StdGen</code> and you most likely won't have to deal with the <code>RandomGen</code>-class at all. The interesting functions are those of the class <code>Random</code>, in particular <code>random</code> and <code>randomR</code>. They are implemented for a few types like <code>Bool</code>, <code>Char</code>, <code>Int</code> etc. so you can use them to generate different random things than numbers. The function <code>randomR</code> returns a random number in a specified range, so that we can conveniently write import {{Haskell库|p=random|v=1.0.0.0|System|Random}}<syntaxhighlight lang="haskell"> rollDie :: StdGen -> (Int, StdGen) rollDie = randomR (1,6) </syntaxhighlight> As a final note, you may want to compare random number creation in Haskell to its counterpart in imperative languages like C. In the latter, there usually is a "function" <code>rand()</code> that returns a different and random result at each call but internally updates the random seed. Since Haskell is pure, the result of a function is determined solely by its parameters and manipulating the random seed has to manifest itself in the type. {{练习|1= # Roll two dice! With <code>sumTwoDice</code> that is :-) . Use <code>fst</code> to extract the result. # Write a function <code>rollNDice :: Int -> Seed -> ([Int],Seed)</code> that rolls dice n times and returns a list of the n results. ''Extra:'' If you know about infinite lists, use <code>unfoldr</code> and <code>take</code> to get the result list (but without seed this time). # Reimplement <code>Seed</code> and <code>rollDie</code> with <code>StdGen</code> and <code>random</code> from {{Haskell库|p=random|v=1.0.0.0|System|Random}}. # Now that you have random numbers, do some statistical experiments with the help of <code>rollNDice</code>. For example, do a sanity check that <code>rollDie</code> is not skewed and returns each number with equal likelyhood. How is the sum of pips of a double dice roll distributed? The difference? And triple rolls? }} === Threading the State with ''bind'' === <i><code>>></code> for the state monad is easier than <code>>>=</code>. But it's meaningless for random numbers :-/ PICTUREs for the plumbing! Somehow shorten the discussion, mainly introduce <code>return</code> more fluently. Expanding the definitions of the new combinators as exercises to check that the new code for sumTwoDice is the same as the old one.</i> In the last subsection, we've seen that state transformers like random number generators can be modeled by functions <code>s -> (a,s)</code> where <code>s</code> is the type of the state. Such a function takes a state and returns a result of type <code>a</code> and a transformed state. However, programming with these functions is a bit tedious since we have to explicitly pass the state from one computation to the next one like in the definition of <code>sumTwoDice</code><syntaxhighlight lang="haskell"> sumTwoDice :: Seed -> (Int, Seed) sumTwoDice = \seed0 -> let (die1, seed1) = rollDie seed0 (die2, seed2) = rollDie seed1 in (die1 + die2, seed2) </syntaxhighlight>Each state has to be named and we have to take care to not pass the wrong state to the next function by accident. Of course, we are Haskell programmers: if there are common patterns or boilerplate in our code, we should search for a way to abstract and capture them in a higher order function. Thus, we want to find something that can combine state transformers <code>s -> (a,s)</code> to larger ones by passing the state from one to the next. A first attempt is an operator named "'''then'''"<syntaxhighlight lang="haskell"> (>>) :: (Seed -> (a,Seed)) -> (Seed -> (b,Seed)) -> (Seed -> (b,Seed)) </syntaxhighlight>which passes the state from the first computation to the second<syntaxhighlight lang="haskell"> (>>) m n = \seed0 -> let (result1, seed1) = m seed0 (result2, seed2) = n seed1 in (result2, seed2) </syntaxhighlight>By nesting it, we can already roll a die multiple times<syntaxhighlight lang="haskell"> rollDie >> (rollDie >> rollDie) </syntaxhighlight>without seeing a single state! Unfortunately, <code>(>>)</code> doesn't allow us to use the result of the first die roll in the following ones, it's simply ignored. In other words, this combinaton changes the random seed three times but only returns the pips from the last die roll. Rather pointless for random numbers, but we're on the right track. PICTURE FOR <code>(>>)</code>! We somehow need a way to pass the result from the first computation to the second, "then" is not yet general enough to allow the implementation of <code>sumTwoDice</code>. But first, we should introduce a type synonym to simplify the type signatures<syntaxhighlight lang="haskell"> type Random a = Seed -> (a, Seed) (>>) :: Random a -> Random b -> Random b rollDie :: Random Int sumTwoDice :: Random Int </syntaxhighlight>Astonishingly, this gives an entirely new point of view: a value of type <code>Random a</code> can be seen as a value of type <code>a</code> that varies randomly. So, <code>rollDie</code> can be interpreted as a number between 1 and 6 that "fidgets" and is sometimes "here" and sometimes "there" when asked about is value. We will explore this idea further, but for now, let's stick to our initial goal that <code>Random a</code> is a simple shortcut for a state transformer. Just take a mental note about the observation that our aim of explicitely removing the state from our functions naturally asks for removing the state from our types, too. Now, how to pass the result from one computation to the next? Well, we may simply give it as parameter to the next one<syntaxhighlight lang="haskell"> (>>=) :: Random a -> (a -> Random b) -> Random b </syntaxhighlight>In other words, the second state transformer is now replaced by a function so that its result of type <code>b</code> may depend on the previous result <code>a</code>. The implementation is almost that of <code>>></code><syntaxhighlight lang="haskell"> (>>=) m g = \seed0 -> let (result1, seed1) = m seed0 (result2, seed2) = (g result1) seed1 in (result2, seed2) </syntaxhighlight>with the only difference being that <code>g</code> now takes <code>result1</code> as parameter. <i>PICTURE!</i> This combinator named "'''bind'''" should finally allow us to implement <code>sumTwoDice</code>. Let's see: we roll the first die and feed the result to a function that adds a second die roll to that<syntaxhighlight lang="haskell"> sumTwoDice :: Random Int sumTwoDice = rollDie >>= (\die1 -> addToDie die1) </syntaxhighlight>Adding the second die roll uses the remaining code from our original definition of <code>sumTwoDice</code>.<syntaxhighlight lang="haskell"> addToDie :: Int -> Random Int addToDie die1 = \seed1 -> let (die2, seed2) = rollDie seed1 in (die1 + die2, seed2) </syntaxhighlight>(Remember that <code>Random Int = Seed -> (Int, Seed)</code>.) That's still unsatisfactory, since we would like to avoid all explicit state and just use <code>>>=</code> a second time to feed the second dice roll to the sum<syntaxhighlight lang="haskell"> addToDie die1 = rollDie >>= (\die2 -> addThem die2) where addThem die2 = \seed2 -> (die1 + die2, seed2) </syntaxhighlight>That's the same as<syntaxhighlight lang="haskell"> addToDie die1 = rollDie >>= (\die2 -> (\seed2 -> (die1 + die2, seed2))) </syntaxhighlight>which is almost<syntaxhighlight lang="haskell"> addToDie die1 = rollDie >>= (\die2 -> (die1 + die2)) </syntaxhighlight>though not quite since the latter doesn't type check since the sum has type <code>Int</code> instead of the expected <code>Random Int</code>. But we can convert the former into the latter with a helper function called "'''return'''"!<syntaxhighlight lang="haskell"> addToDie die1 = rollDie >>= (\die2 -> return (die1 + die2)) return :: a -> Random a return x = \seed0 -> (x, seed0) </syntaxhighlight>So, <code>return</code> doesn't change the state but simply ''returns'' its argument as result. For random numbers, this means that <code>return</code> creates a number that isn't random at all. Last but not least, we can drop the definition of <code>addToDie</code> and directly write<syntaxhighlight lang="haskell"> sumTwoDice :: Random Int sumTwoDice = rollDie >>= (\die1 -> rollDie >>= (\die2 -> return (die1 + die2))) </syntaxhighlight>{{练习|1= #Implement <code>rollNDice :: Int -> Random [Int]</code> from the previous subsection with <code>>>=</code> and <code>return</code>. NOTE: Since <code>>>=</code> and <code>return</code> are already present in the {{Haskell库|Prelude}}, you may want to use <code>import Prelude hiding ((>>=),return)</code> to avoid compilation errors.}} To conclude, the quest of automating the passing of state from one computation to the next led us to the two operations that define a monad. Of course, this is just the beginning. The reader is probably not yet accustomed to the <code>>>=</code>-combinator, how to program with it effectively? What about the three monad laws mentioned in the introduction? But before we embark to answer these questions in the next big section, let us emphasize the need for using <code>>>=</code> as a main primitive in a slightly different example in the next subsection. === Input/Output needs ''bind'' === <i><code>IO</code> is the one type that requires the programmer to know what a monad is, the other monads are more or less optional. It makes abstract <code>return</code> and <code>bind</code> necessary. Explaining <code>World -> (a, World) = IO a</code> and the need to hide the <code>World</code> naturally leads to <code>return</code> and <code>>>=</code>. I guess we need to mention somewhere that <code>main :: IO ()</code> is the link to the real world. </i> Performing input/output in a purely functional language like Haskell has long been a fundamental problem. How to implement operations like <code>getChar</code> which returns the latest character that the user has typed or <code>putChar c</code> which prints the character <code>c</code> on the screen? Giving <code>getChar</code> the type <code>getChar :: Char</code> is not an option, since a pure function with no arguments must be constant. We somehow have to capture that <code>getChar</code> also performs the '''side effect''' of interacting with the user. Likewise, a type <code>putChar :: Char -> ()</code> is useless since the only value this function can return has to be <code>()</code>. The breakthrough came when it was realized<ref>{{引用日志|author=Simon Peyton Jones|coauthors=Philip Wadler|date=1993|title=Imperative functional programming|journal=20'th Symposium on Principles of Programming Languages|url=http://homepages.inf.ed.ac.uk/wadler/topics/monads.html#imperative}}</ref> that monads, i.e. the operations <code>>>=</code> and <code>return</code> can be used to elegantly deal with side effects. The idea is to give our two primitive operations the types<syntaxhighlight lang="haskell"> getChar :: IO Char putChar :: Char -> IO () </syntaxhighlight>and interpret a value of type <code>IO a</code> as a '''computation''' or '''action''' that performs a side effect before returning the value <code>a</code>. This is rather abstract, so a more concrete way is to interpret <code>IO</code> as a state transformer<syntaxhighlight lang="haskell"> type IO a = World -> (a, World) </syntaxhighlight>that acts on and changes the "state of the world". In other words, printing a character takes the world and returns one where the character has been printed and reading a character returns a world where the character has been read. With this model, an action <code>echo :: IO ()</code> that reads a character and immediately prints it to the screen would be written as<syntaxhighlight lang="haskell"> echo = \world0 -> let (c , world1) = getChar world0 ((), world2) = putChar c world1 in ((), world2) </syntaxhighlight>Of course, this is a case for the ''bind'' combinator that passes the state of the world for us:<syntaxhighlight lang="haskell"> echo = getChar >>= putChar </syntaxhighlight> But for <code>IO a</code>, the use of <code>>>=</code> is not a convenience, it is <em>mandatory</em>. This is because by passing around the world explicitly, we could write (either accidentally or even consciously) something that duplicates the world:<syntaxhighlight lang="haskell"> havoc = \world0 -> let (c , world1) = getChar world0 ((), world2) = putChar c world0 in ((), world2) </syntaxhighlight>Now, where does <code>putChar</code> get the character <code>c</code> from? Did the state of world roll back similar to a time travel? This makes no sense, we have to ensure that the world is used in a single-threaded way. But this is easy to achieve: we just make <code>IO a</code> an abstract data type and export only <code>>>=</code> and <code>return</code> for combining actions, together with primitive operations like <code>putChar</code>. There's even more: the model <code>World -> (a,World)</code> for input/output just doesn't work, one of the exercises shows why. Also, there is no hope to extend it to concurrency and exceptions. In other words, it is imperative to make <code>>>=</code> for composing effectful computations <code>IO a</code> an abstract primitive operation. {{练习|1=<ol> <li>Write a function <code>putString :: String -> IO ()</code> that outputs a sequence of characters with the help of <code>putChar</code>. </li><li>The program loop :: IO () loop = return () >> loop loops forever whereas loopX :: IO () loopX = putChar 'X' >> loopX prints an infinite sequence <code>XXXXXX...</code> of <code>X</code>-s. Clearly, a user can easily distinguish them by looking on the screen. However, show that the model <code>IO a = World -> (a, World)</code> gives the same [[Haskell/Denotational semantics|denotation]] ⊥ for both. This means that we have to abandon this model as possible semantics for <code>IO a</code>. </li></ol>}} == Programming with <i>bind</i> and <i>return</i> == <i>Time to write programs! More complicated stuff for <code>Random a</code>. Examples to code: St.Petersburg paradox, Lewis Carroll's pillow problem. Somewhere make explicit instances of the <code>Monad</code>-class? Hm, we really need to incorporate the monad class in the type signatures. I'm not sure whether the nuclear waste metaphor is necessary?</i> In the last section, we showed how the two defining operations <code>>>=</code> and <code>return</code> of a monad arise as abstraction for composing state transformers. We now want to focus on how to program effectively with these. === Nuclear Waste Containers === <i><code>Random a</code> as fuzzy <code>a</code>. Programming would be so much easier if we had <code>extract :: Random a -> a</code>, bind is sooo unwieldy. Mental prevention: think of monads as "Nuclear waste containers", the waste may not leak outside at any cost. The thing closest to <code>extract</code> we can have is <code>join :: m (m a) -> m a</code>. The text probably talks too much about "monads as containers", I'm not sure what to do.</i> We saw that the bind operation takes a computation, executes it, and feeds its result to the next, like in<syntaxhighlight lang="haskell"> echo = getChar >>= \char -> putChar char sumTwoDice = rollDie >>= \die1 -> rollDie >>= \die2 -> return (die1 + die2) </syntaxhighlight>(Note that for parsing, lambda expressions extend as far to the right as possible, so it's not necessary to put them in parantheses.) However, it could be tempting to "execute" a monadic action like <code>IO a</code> with some hypothetical function<syntaxhighlight lang="haskell"> extract :: IO a -> a </syntaxhighlight>in order to conveniently formulate<syntaxhighlight lang="haskell"> echo = return (putChar (extract getChar)) </syntaxhighlight>Of course, such a function does not make sense. For state transformers like <code>Random a = Seed -> (a, Seed)</code>, it would have to invent a state and discard it again, thus regressing from our goal of passing the new state to the next computation. Here's a metaphor to strengthen your mind against <code>''extract''</code>: *Think of a monadic computation <code>M a</code> as a container for a value of type <code>a</code> that is unfortunately paired with highly dangerous '''nuclear waste'''. Under no circumstance should this tightly sealed container be opened to <code>''extract''</code> the <code>a</code> or the nuclear waste will leak out, resulting in a catastrophe!. So, there are some like <code>getChar :: IO Char</code> or <code>rollDie :: Random Int</code> that produce a precious value but unfortunately cannot operate without tainting it with nuclear waste. But fortunately, we have our function<syntaxhighlight lang="haskell"> (>>=) :: Monad m => m a -> (a -> m b) -> m b </syntaxhighlight>that nonetheless allows us to operate on the value contained in <code>M a</code> by entering the container and applying the given function to the value inside it. This way, eveything happens inside the container and no nuclear materials leak out. Arguably, this description of "bind" probably applies better to a function<syntaxhighlight lang="haskell"> fmap :: Monad m => (a -> b) -> m a -> m b fmap f m = m >>= \x -> return (f x) </syntaxhighlight>that takes a pure function into the container to transform the value within. You may notice that this is the defining mapping for [[Haskell/Category theory|functors]], i.e. every monad is a functor. Apparently, <code>fmap</code> is less general than <code>>>=</code> since the latter expects the function to be lifted into the container to produce nuclear waste, too. The best what <code>fmap</code> can do is<syntaxhighlight lang="haskell"> fmap' :: Monad m => (a -> (m b)) -> m a -> m (m b) </syntaxhighlight>to produce a nested container. Of course, it is safe to open the inner container since the outer container still shields the environment from the nuclear waste<syntaxhighlight lang="haskell"> join :: Monad m => m (m a) -> m a join m = m >>= id </syntaxhighlight>In other words, we can describe the operation of <code>>>=</code> as<syntaxhighlight lang="haskell"> m >>= g = join (fmap g m) </syntaxhighlight>i.e. it lifts a waste-producing computation into the container and flattens the resulting nested containers. <i>We will explore this futher in [[#Monads as Containers]].</i> Of course, we shouldn't take the nuclear waste metaphor too literally, since there usually is some way to "run" the computation. For instance, random numbers can be observed as an infinite list of random numbers produced by an initial random seed.<syntaxhighlight lang="haskell"> run :: Random a -> (Seed -> [a]) </syntaxhighlight>Only the <code>IO</code> monad is a primitive in Haskell. How do we "run" it, then? The answer is that the link of a Haskell program to outside world is the function<syntaxhighlight lang="haskell"> main :: IO () </syntaxhighlight>which will be run by the operating system. In other words, the Haskell program itself ultimately produces nuclear waste, so there is no need to extract <code>IO a -> a</code>. {{练习|1= # Implement <code>run</code> with <code>unfoldr</code>. }} === <code>do</code>-Notation === A common way to write the composition of multiple monadic computations is<syntaxhighlight lang="haskell"> sumTwoDice = do die1 <- rollDie die2 <- rollDie return (die1 + die2) </syntaxhighlight> === Control Structures === <i>Needs a better title. Introduce <code>sequence, fmap, liftMn, forM, mapM</code> and friends. </i> === The three Monad Laws === <i>In the state monad, <code>return</code> doesn't touch the state. That can be formulated abstractly with the first two monad laws. Hm, what about the third? How to motivate that? The third is intuitive: in the context of passing things around, order, not grouping matters (associativity).</i> == Monads as containers == <i>Needs a better title. Introduce the second instances of monads, namely <code>[a]</code> and <code>Maybe a</code>. Shows that the operations return and bind are applicable to quite a range of problems. The more "exotic" example <pre>data Tree a = Leaf a | Branch (Tree a) (Tree a)</pre> belongs here, too, probably as exercise. </i> === Lists === <i><code>concatMap</code> and <code>sequence</code>.</i>. === Maybe === <i>Maybe <code>Either</code>, too? == References == <references /> {{Haskell导航|chapter=Monads}} [[Category:Haskell|理解monads]]
该页面使用的模板:
Template:Haskell导航
(
查看源代码
)
Template:Haskell库
(
查看源代码
)
Template:Haskell微型目录
(
查看源代码
)
Template:信息
(
查看源代码
)
Template:引用日志
(
查看源代码
)
Template:练习
(
查看源代码
)
Template:警告
(
查看源代码
)
返回
Haskell/理解 Monad
。
导航菜单
个人工具
登录
命名空间
页面
讨论
不转换
查看
阅读
查看源代码
查看历史
更多
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
特殊页面
工具
链入页面
相关更改
页面信息