3 ms·
A monad is a computational context, where the nature of that context is determined by two things: the shape of the data structure corresponding to it, and the d
by johnday 4y ago
A monad is a computational context, where the nature of that context is determined by two things: the shape of the data structure corresponding to it, and the definition of (>>=) which handles sequencing of two computations in that context.
Anything more specific than that should be handled case-by-case until you build an intuition for how any given monad will behave.
- TedDoesntTalk 4y agoIs computational context another way of saying scope?
- trenchgun 4y agoNot exactly. Computational context in this sense is like, is this computation of the type that can either fail or succeed? (Maybe monad). Or is this computation of the type that can produce multiple values of the same type? (List monad). Or maybe this computation can produce value of one type or another (Either monad). Or a computation that can interacti with inputs and outputs (IO monad). So these are computations in differend kinds of computational "worlds" in a sense, with different rules on how such a computation in this context composes.
- hackinthebochs 4y agoLet me rephrase it in words that I have more intuitive grasp of and see if the translation holds. A monad is a way to pick out the specific constraints of interest and define a class of computations that satisfy those constraints, and how computations within this class compose. Are the constraints limited to return values or can there be other kinds of constraints?
- bradrn 4y agoNot really… unless you have a different definition of the word ‘constraint’ than I do. Personally, I don’t see ‘constraints’ as having anything to do with monads: in fact, I see the ultimate goal of monads as allowing you to relax various constraints a language imposes on your code — things like ‘execution proceeds from one line to the next’, or in Haskell ‘expressions have no side-effects’. It might be more helpful to think about this in terms of actual code samples. Consider a data type which can represent a computation which can fail. This data type has two possible states: ‘Failed’, and ‘Success(return_value)’ (where the ‘return_value’ field can be anything). Now, let’s say you have one result which might have failed, and you want to pass it to another fallible computation, which we can represent as a function from an input to a fallible output. How do you do it? Well, you might define a function something like this (in vaguely Rust-like pseudocode): fn andThen(value, nextComputation) { match value { Success(return_value) => return nextComputation(returnValue); Failed => return Failed; } } That is, continue on with the next computation if you have a result, and short-circuit otherwise. Now let’s consider a different case: what if your computation is instead nondeterministic, and can return multiple results? In this case, you might instead have a nondeterministic value — let’s represent it as a list — which you got from one computation, and you need to supply it as input to another nondeterministic computation. Then you’ll need to write something like: fn andThen_nondet(value, nextComputation) { mut result = []; foreach (v in value) { result.concat(nextComputation(v)); } return result; } Or a third case — promises for asynchronous values! Which in most languages have an ‘andThen’ method too, allowing you to take one asynchronous value and pass that value to an asynchronous computation. I’ll skip an example for this one since it’s so well-known, but obviously this ‘andThen’ method has roughly the same structure as the others I’ve shown. Monads, therefore, are nothing special — they’re just data types with an ‘andThen’ method to let you sequence them. The method always has the same form: inspect the input to temporarily separate any returned value(s) from their context, then call the next computation to wrap those value(s) back up in the required context. This gets neatly summarised in the Haskell type signature: (>>=) :: Monad m => m a -> (a -> m b) -> m b
- deleted 4y ago[deleted]
- deleted 4y ago[deleted]
- acjohnson55 4y agoThat's more or less how I think of it. Each bind/flatMap introduces a new value into scope and can consume data in the form of the result of a further bind/flatMap or final return. It's essentially a mini interpreter for tiny functionally pure segments of a program. One tricky part of the interpretation is understanding the most familiar monadic data types in this paradigm. For example, the list monad representing discrete nondeterminism. Writing about this so tersely is probably only going to confuse anyone who isn't already 90% of the way to understanding....