Haskell: Computation "in a monad" -- meaning?
Asked Answered
F

5

16

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")

Fissi answered 22/2, 2013 at 3:30 Comment(2)
Nowadays I prefer to refer to these as actions or computations. If they are produced by a function, you might talk about parametric actions or monadic functions, but the latter term is ambiguous. It could refer to a -> m b, but also to m (a -> b). Since this last one is used in applicative style I prefer a less ambiguous term.Millstream
Be sure not to miss C. A. McCann's answer below.Millstream
M
12

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).

Maliamalice answered 22/2, 2013 at 15:4 Comment(4)
Thanks for this discussion, that produced some headway :-). At this stage, in reading your discussion, I think I'm stuck on not being clear as to what exactly "THE monad" refers to (something that has layers and depth?), in a scenario that involves possibly multiple functions that each return monadic values, composed with help of fmap and join, and their own monadic results.Fissi
@gwideman: In the strictest sense, "a monad" in Haskell is the mathematical structure described by a type constructor and appropriate implementations of fmap, return, and join, and "the monad" would be the specific type constructor and instance being discussed. In practice, the term is often also abused slightly to refer to the data constructors associated with the type constructor and/or values built with them.Maliamalice
My conclusion: "Computation in 'the' Xyz Monad" means "computation in functions which return values of type Xyz, with said functions composed together using the fmap/return/join methods of the Xyz Monad instance".Fissi
@gwiderman Take a look at Free monads in the package free to see how all monads have a kind of interpretation as "something with layers and depth". Gabriel Gonzalez has a good explanation of this. Note that Free monads are not the "one true way" of thinking of monads, instead they have a different universal property—for any monad m, there always exists a corresponding free monad Free m and a unique function Free m -> m.Arrowood
A
13

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".

Arrowood answered 22/2, 2013 at 4:52 Comment(3)
Thanks. "For conversation's sake" suggests "in the monad" is just vague figure of speech, but I'm not quite satisfied. "So funcs like >>= [...] return..." My trouble: >>= and return chain functions that take non-monadic values and return monadic values: (func outputs monadic value) -> (extract enclosed val using >>=) -> (process val in func f) -> (wrap outval in monad). "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.Fissi
@Fissi something to think about: one of your examples you linked to uses a state monad, which is an example where bind is really composition: it's building a stateful computation to be finally "run" on some initial state.Cecrops
I'd say those are "in the monad" because (1) they never return a bare value for you to get a hold of "outside" and (2) when you bind them with >>= you're basically injecting them into the Monad and then using join :: Monad m => m (m a) -> m a to merge the layers back together.Arrowood
M
12

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).

Maliamalice answered 22/2, 2013 at 15:4 Comment(4)
Thanks for this discussion, that produced some headway :-). At this stage, in reading your discussion, I think I'm stuck on not being clear as to what exactly "THE monad" refers to (something that has layers and depth?), in a scenario that involves possibly multiple functions that each return monadic values, composed with help of fmap and join, and their own monadic results.Fissi
@gwideman: In the strictest sense, "a monad" in Haskell is the mathematical structure described by a type constructor and appropriate implementations of fmap, return, and join, and "the monad" would be the specific type constructor and instance being discussed. In practice, the term is often also abused slightly to refer to the data constructors associated with the type constructor and/or values built with them.Maliamalice
My conclusion: "Computation in 'the' Xyz Monad" means "computation in functions which return values of type Xyz, with said functions composed together using the fmap/return/join methods of the Xyz Monad instance".Fissi
@gwiderman Take a look at Free monads in the package free to see how all monads have a kind of interpretation as "something with layers and depth". Gabriel Gonzalez has a good explanation of this. Note that Free monads are not the "one true way" of thinking of monads, instead they have a different universal property—for any monad m, there always exists a corresponding free monad Free m and a unique function Free m -> m.Arrowood
R
4

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".

Recover answered 22/2, 2013 at 7:46 Comment(4)
Thanks for the effort, but I don't see where this bears on my question. The two instances where you discuss "in"-ness don't make sense to me, but maybe I missed something. Specifically: "So you wrap your existing computation in the maybe monad". (a) this computation doesn't calculate distance, it simply returns undefined, so I'm not sure what was being illustrated. (b) It doesn't "warp the computation" in a monad, it just specifies that the input and output values are constrained to be of type Maybe, which can perform as a Monad if suitable definitions are included,...Fissi
[cont'd] though in this example that capability doesn't seem to be used. "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". That seems more like a non-sequitur. If the computation is independent of the monad, how does this imply that the computation is inside the monad?Fissi
(a) I was just too lazy to type. I thought you know how to implement this (and the last code block contains an implementation which would work for the specific monads, too) (b) You are working with values wrapped in a monad, but as I tried to show this doesn't affect the computation much. This makes the outside monad interchangeable, which makes it in turn necessary to mention it somehow when talking about it. If your calculation were more "hardcoded" towards a certain monad like Maybe, you wouldn't bother to stress that fact.Recover
And I think you are interpreting way too much philosophical implications into this wording...Recover
C
2

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.

Cupellation answered 22/2, 2013 at 11:51 Comment(1)
Thanks for the comments Boris. The question was about "computation in the [something] monad", not just "computation in". There are several instances of the former in each of the referenced links.Fissi
E
1

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.

Extravehicular answered 22/2, 2013 at 3:50 Comment(2)
This is indeed an example of the kind of comment in which I don't understand how the preposition "in" applies.Fissi
Well we need to combine two noun phrases "computation" and "the IO monad". English only has a limited number of prepositions. "Belonging to" might make more sense than "in".Extravehicular

© 2022 - 2024 — McMap. All rights reserved.