What is a monad?

前端 未结 30 1458
太阳男子
太阳男子 2020-11-30 13:39

Having briefly looked at Haskell recently, what would be a brief, succinct, practical explanation as to what a monad essentially is?

I have found most expla

相关标签:
30条回答
  • 2020-11-30 14:09

    A monad is a thing used to encapsulate objects that have changing state. It is most often encountered in languages that otherwise do not allow you to have modifiable state (e.g., Haskell).

    An example would be for file I/O.

    You would be able to use a monad for file I/O to isolate the changing state nature to just the code that used the Monad. The code inside the Monad can effectively ignore the changing state of the world outside the Monad - this makes it a lot easier to reason about the overall effect of your program.

    0 讨论(0)
  • 2020-11-30 14:10

    In addition to the excellent answers above, let me offer you a link to the following article (by Patrick Thomson) which explains monads by relating the concept to the JavaScript library jQuery (and its way of using "method chaining" to manipulate the DOM): jQuery is a Monad

    The jQuery documentation itself doesn't refer to the term "monad" but talks about the "builder pattern" which is probably more familiar. This doesn't change the fact that you have a proper monad there maybe without even realizing it.

    0 讨论(0)
  • 2020-11-30 14:10

    In the context of Scala you will find the following to be the simplest definition. Basically flatMap (or bind) is 'associative' and there exists an identity.

    trait M[+A] {
      def flatMap[B](f: A => M[B]): M[B] // AKA bind
    
      // Pseudo Meta Code
      def isValidMonad: Boolean = {
        // for every parameter the following holds
        def isAssociativeOn[X, Y, Z](x: M[X], f: X => M[Y], g: Y => M[Z]): Boolean =
          x.flatMap(f).flatMap(g) == x.flatMap(f(_).flatMap(g))
    
        // for every parameter X and x, there exists an id
        // such that the following holds
        def isAnIdentity[X](x: M[X], id: X => M[X]): Boolean =
          x.flatMap(id) == x
      }
    }
    

    E.g.

    // These could be any functions
    val f: Int => Option[String] = number => if (number == 7) Some("hello") else None
    val g: String => Option[Double] = string => Some(3.14)
    
    // Observe these are identical. Since Option is a Monad 
    // they will always be identical no matter what the functions are
    scala> Some(7).flatMap(f).flatMap(g)
    res211: Option[Double] = Some(3.14)
    
    scala> Some(7).flatMap(f(_).flatMap(g))
    res212: Option[Double] = Some(3.14)
    
    
    // As Option is a Monad, there exists an identity:
    val id: Int => Option[Int] = x => Some(x)
    
    // Observe these are identical
    scala> Some(7).flatMap(id)
    res213: Option[Int] = Some(7)
    
    scala> Some(7)
    res214: Some[Int] = Some(7)
    

    NOTE Strictly speaking the definition of a Monad in functional programming is not the same as the definition of a Monad in Category Theory, which is defined in turns of map and flatten. Though they are kind of equivalent under certain mappings. This presentations is very good: http://www.slideshare.net/samthemonad/monad-presentation-scala-as-a-category

    0 讨论(0)
  • 2020-11-30 14:11

    Monads are to control flow what abstract data types are to data.

    In other words, many developers are comfortable with the idea of Sets, Lists, Dictionaries (or Hashes, or Maps), and Trees. Within those data types there are many special cases (for instance InsertionOrderPreservingIdentityHashMap).

    However, when confronted with program "flow" many developers haven't been exposed to many more constructs than if, switch/case, do, while, goto (grr), and (maybe) closures.

    So, a monad is simply a control flow construct. A better phrase to replace monad would be 'control type'.

    As such, a monad has slots for control logic, or statements, or functions - the equivalent in data structures would be to say that some data structures allow you to add data, and remove it.

    For example, the "if" monad:

    if( clause ) then block
    

    at its simplest has two slots - a clause, and a block. The if monad is usually built to evaluate the result of the clause, and if not false, evaluate the block. Many developers are not introduced to monads when they learn 'if', and it just isn't necessary to understand monads to write effective logic.

    Monads can become more complicated, in the same way that data structures can become more complicated, but there are many broad categories of monad that may have similar semantics, but differing implementations and syntax.

    Of course, in the same way that data structures may be iterated over, or traversed, monads may be evaluated.

    Compilers may or may not have support for user-defined monads. Haskell certainly does. Ioke has some similar capabilities, although the term monad is not used in the language.

    0 讨论(0)
  • 2020-11-30 14:12

    I will try to explain Monad in the context of Haskell.

    In functional programming, function composition is important. It allows our program to consist of small, easy-to-read functions.

    Let's say we have two functions: g :: Int -> String and f :: String -> Bool.

    We can do (f . g) x, which is just the same as f (g x), where x is an Int value.

    When doing composition/applying the result of one function to another, having the types match up is important. In the above case, the type of the result returned by g must be the same as the type accepted by f.

    But sometimes values are in contexts, and this makes it a bit less easy to line up types. (Having values in contexts is very useful. For example, the Maybe Int type represents an Int value that may not be there, the IO String type represents a String value that is there as a result of performing some side effects.)

    Let's say we now have g1 :: Int -> Maybe String and f1 :: String -> Maybe Bool. g1 and f1 are very similar to g and f respectively.

    We can't do (f1 . g1) x or f1 (g1 x), where x is an Int value. The type of the result returned by g1 is not what f1 expects.

    We could compose f and g with the . operator, but now we can't compose f1 and g1 with .. The problem is that we can't straightforwardly pass a value in a context to a function that expects a value that is not in a context.

    Wouldn't it be nice if we introduce an operator to compose g1 and f1, such that we can write (f1 OPERATOR g1) x? g1 returns a value in a context. The value will be taken out of context and applied to f1. And yes, we have such an operator. It's <=<.

    We also have the >>= operator that does for us the exact same thing, though in a slightly different syntax.

    We write: g1 x >>= f1. g1 x is a Maybe Int value. The >>= operator helps take that Int value out of the "perhaps-not-there" context, and apply it to f1. The result of f1, which is a Maybe Bool, will be the result of the entire >>= operation.

    And finally, why is Monad useful? Because Monad is the type class that defines the >>= operator, very much the same as the Eq type class that defines the == and /= operators.

    To conclude, the Monad type class defines the >>= operator that allows us to pass values in a context (we call these monadic values) to functions that don't expect values in a context. The context will be taken care of.

    If there is one thing to remember here, it is that Monads allow function composition that involves values in contexts.

    0 讨论(0)
  • 2020-11-30 14:13

    After much striving, I think I finally understand the monad. After rereading my own lengthy critique of the overwhelmingly top voted answer, I will offer this explanation.

    There are three questions that need to be answered to understand monads:

    1. Why do you need a monad?
    2. What is a monad?
    3. How is a monad implemented?

    As I noted in my original comments, too many monad explanations get caught up in question number 3, without, and before really adequately covering question 2, or question 1.

    Why do you need a monad?

    Pure functional languages like Haskell are different from imperative languages like C, or Java in that, a pure functional program is not necessarily executed in a specific order, one step at a time. A Haskell program is more akin to a mathematical function, in which you may solve the "equation" in any number of potential orders. This confers a number of benefits, among which is that it eliminates the possibility of certain kinds of bugs, particularly those relating to things like "state".

    However, there are certain problems that are not so straightforward to solve with this style of programming. Some things, like console programming, and file i/o, need things to happen in a particular order, or need to maintain state. One way to deal with this problem is to create a kind of object that represents the state of a computation, and a series of functions that take a state object as input, and return a new modified state object.

    So let's create a hypothetical "state" value, that represents the state of a console screen. exactly how this value is constructed is not important, but let's say it's an array of byte length ascii characters that represents what is currently visible on the screen, and an array that represents the last line of input entered by the user, in pseudocode. We've defined some functions that take console state, modify it, and return a new console state.

    consolestate MyConsole = new consolestate;
    

    So to do console programming, but in a pure functional manner, you would need to nest a lot of function calls inside eachother.

    consolestate FinalConsole = print(input(print(myconsole, "Hello, what's your name?")),"hello, %inputbuffer%!");
    

    Programming in this way keeps the "pure" functional style, while forcing changes to the console to happen in a particular order. But, we'll probably want to do more than just a few operations at a time like in the above example. Nesting functions in that way will start to become ungainly. What we want, is code that does essentially the same thing as above, but is written a bit more like this:

    consolestate FinalConsole = myconsole:
                                print("Hello, what's your name?"):
                                input():
                                print("hello, %inputbuffer%!");
    

    This would indeed be a more convenient way to write it. How do we do that though?

    What is a monad?

    Once you have a type (such as consolestate) that you define along with a bunch of functions designed specifically to operate on that type, you can turn the whole package of these things into a "monad" by defining an operator like : (bind) that automatically feeds return values on its left, into function parameters on its right, and a lift operator that turns normal functions, into functions that work with that specific kind of bind operator.

    How is a monad implemented?

    See other answers, that seem quite free to jump into the details of that.

    0 讨论(0)
提交回复
热议问题