What is a monad?

前端 未结 30 1595
太阳男子
太阳男子 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

    After giving an answer to this question a few years ago, I believe I can improve and simplify that response with...

    A monad is a function composition technique that externalizes treatment for some input scenarios using a composing function, bind, to pre-process input during composition.

    In normal composition, the function, compose (>>), is use to apply the composed function to the result of its predecessor in sequence. Importantly, the function being composed is required to handle all scenarios of its input.

    (x -> y) >> (y -> z)

    This design can be improved by restructuring the input so that relevant states are more easily interrogated. So, instead of simply y the value can become Mb such as, for instance, (is_OK, b) if y included a notion of validity.

    For example, when the input is only possibly a number, instead of returning a string which can contain dutifully contain a number or not, you could restructure the type into a bool indicating the presence of a valid number and a number in tuple such as, bool * float. The composed functions would now no longer need to parse an input string to determine whether a number exists but could merely inspect the bool portion of a tuple.

    (Ma -> Mb) >> (Mb -> Mc)

    Here, again, composition occurs naturally with compose and so each function must handle all scenarios of its input individually, though doing so is now much easier.

    However, what if we could externalize the effort of interrogation for those times where handling a scenario is routine. For example, what if our program does nothing when the input is not OK as in when is_OK is false. If that were done then composed functions would not need to handle that scenario themselves, dramatically simplifying their code and effecting another level of reuse.

    To achieve this externalization we could use a function, bind (>>=), to perform the composition instead of compose. As such, instead of simply transferring values from the output of one function to the input of another Bind would inspect the M portion of Ma and decide whether and how to apply the composed function to the a. Of course, the function bind would be defined specifically for our particular M so as to be able to inspect its structure and perform whatever type of application we want. Nonetheless, the a can be anything since bind merely passes the a uninspected to the the composed function when it determines application necessary. Additionally, the composed functions themselves no longer need to deal with the M portion of the input structure either, simplifying them. Hence...

    (a -> Mb) >>= (b -> Mc) or more succinctly Mb >>= (b -> Mc)

    In short, a monad externalizes and thereby provides standard behaviour around the treatment of certain input scenarios once the input becomes designed to sufficiently expose them. This design is a shell and content model where the shell contains data relevant to the application of the composed function and is interrogated by and remains only available to the bind function.

    Therefore, a monad is three things:

    1. an M shell for holding monad relevant information,
    2. a bind function implemented to make use of this shell information in its application of the composed functions to the content value(s) it finds within the shell, and
    3. composable functions of the form, a -> Mb, producing results that include monadic management data.

    Generally speaking, the input to a function is far more restrictive than its output which may include such things as error conditions; hence, the Mb result structure is generally very useful. For instance, the division operator does not return a number when the divisor is 0.

    Additionally, monads may include wrap functions that wrap values, a, into the monadic type, Ma, and general functions, a -> b, into monadic functions, a -> Mb, by wrapping their results after application. Of course, like bind, such wrap functions are specific to M. An example:

    let return a = [a]
    let lift f a = return (f a)
    

    The design of the bind function presumes immutable data structures and pure functions others things get complex and guarantees cannot be made. As such, there are monadic laws:

    Given...

    M_ 
    return = (a -> Ma)
    f = (a -> Mb)
    g = (b -> Mc)
    

    Then...

    Left Identity  : (return a) >>= f === f a
    Right Identity : Ma >>= return    === Ma
    Associative    : Ma >>= (f >>= g) === Ma >>= ((fun x -> f x) >>= g)
    

    Associativity means that bind preserves the order of evaluation regardless of when bind is applied. That is, in the definition of Associativity above, the force early evaluation of the parenthesized binding of f and g will only result in a function that expects Ma in order to complete the bind. Hence the evaluation of Ma must be determined before its value can become applied to f and that result in turn applied to g.

提交回复
热议问题