Question

In reading about monads, I keep seeing phrases like "computations in the Xyz monad". What does it mean for a computation to be "in" a certain monad?

I think I have a fair grasp on what monads are about: allowing computations to produce outputs that are usually of some expected type, but can alternatively or additionally convey some other information, such as error status, logging info, state and so on, and allow such computations to be chained.

But I don't get how a computation would be said to be "in" a monad. Does this just refer to a function that produces a monadic result?

Examples: (search "computation in")

Was it helpful?

Solution

Generally, a "computation in a monad" means not just a function returning a monadic result, but such a function used inside a do block, or as part of the second argument to (>>=), or anything else equivalent to those. The distinction is relevant to something you said in a comment:

"Computation" occurs in func f, after val extracted from input monad, and before result is wrapped as monad. I don't see how the computation per se is "in" the monad; it seems conspicuously "out" of the monad.

This isn't a bad way to think about it--in fact, do notation encourages it because it's a convenient way to look at things--but it does result in a slightly misleading intuition. Nowhere is anything being "extracted" from a monad. To see why, forget about (>>=)--it's a composite operation that exists to support do notation. The more fundamental definition of a monad are three orthogonal functions:

fmap :: (a -> b) -> (m a -> m b)
return :: a -> m a
join :: m (m a) -> m a

...where m is a monad.

Now think about how to implement (>>=) with these: starting with arguments of type m a and a -> m b, your only option is using fmap to get something of type m (m b), after which you can use join to flatten the nested "layers" to get just m b.

In other words, nothing is being taken "out" of the monad--instead, think of the computation as going deeper into the monad, with successive steps being collapsed into a single layer of the monad.

Note that the monad laws are also much simpler from this perspective--essentially, they say that when join is applied doesn't matter as long as the nesting order is preserved (a form of associativity) and that the monadic layer introduced by return does nothing (an identity value for join).

OTHER TIPS

Does this just refer to a function that produces a monadic result?

Yes, in short.


In long, it's because Monad allows you to inject values into it (via return) but once inside the Monad they're stuck. You have to use some function like evalWriter or runCont which is strictly more specific than Monad to get values back "out".

More than that, Monad (really, its partner, Applicative) is the essence of having a "container" and allowing computations to happen inside of it. That's what (>>=) gives you, the ability to do interesting computations "inside" the Monad.

So functions like Monad m => m a -> (a -> m b) -> m b let you compute with and around and inside a Monad. Functions like Monad m => a -> m a let you inject into the Monad. Functions like m a -> a would let you "escape" the Monad except they don't exist in general (only in specific). So, for conversation's sake we like to talk about functions that have result types like Monad m => m a as being "inside the monad".

Usually monad stuff is easier to grasp when starting with "collection-like" monads as example. Imagine you calculate the distance of two points:

data Point = Point Double Double

distance :: Point -> Point -> Double
distance p1 p2 = undefined

Now you may have a certain context. E.g. one of the points may be "illegal" because it is out of some bounds (e.g. on the screen). So you wrap your existing computation in the Maybe monad:

distance :: Maybe Point -> Maybe Point -> Maybe Double
distance p1 p2 = undefined

You have exactly the same computation, but with the additional feature that there may be "no result" (encoded as Nothing).

Or you have a have a two groups of "possible" points, and need their mutual distances (e.g. to use later the shortest connection). Then the list monad is your "context":

distance :: [Point] -> [Point] -> [Double]
distance p1 p2 = undefined

Or the points are entered by a user, which makes the calculation "nondeterministic" (in the sense that you depend on things in the outside world, which may change), then the IO monad is your friend:

distance :: IO Point -> IO Point -> IO Double
distance p1 p2 = undefined

The computation remains always the same, but happens to take place in a certain "context", which adds some useful aspects (failure, multi-value, nondeterminism). You can even combine these contexts (monad transformers).

You may write a definition that unifies the definitions above, and works for any monad:

 distance :: Monad m => m Point -> m Point -> m Double
 distance p1 p2 = do
     Point x1 y1 <- p1
     Point x2 y2 <- p2
     return $ sqrt ((x1-x2)^2 + (y1-y2)^2)  

That proves that our computation is really independent from the actual monad, which leads to formulations as "x is computed in(-side) the y monad".

Looking at the links you provided, it seems that a common usage of "computation in" is with regards to a single monadic value. Excerpts:

Gentle introduction - here we run a computation in the SM monad, but the computation is the monadic value:

-- run a computation in the SM monad
runSM                   :: S -> SM a -> (a,S)

All about monads - previous computation refers to a monadic value in the sequence:

The >> function is a convenience operator that is used to bind a monadic computation that does not require input from the previous computation in the sequence

Understanding monads - here the first computation could refer to e.g. getLine, a monadic value :

(binding) gives an intrinsic idea of using the result of a computation in another computation, without requiring a notion of running computations.

So as an analogy, if I say i = 4 + 2, then i is the value 6, but it is equally a computation, namely the computation 4 + 2. It seems the linked pages uses computation in this sense - computation as a monadic value - at least some of the time, in which case it makes sense to use the expression "a computation in" the given monad.

Consider the IO monad. A value of type IO a is a description of a large (often infinite) number of behaviours where a behaviour is a sequence of IO events (reads, writes, etc). Such a value is called a "computation"; in this case it is a computation in the IO monad.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top