1

I don't understand why I can't decompose a (let's say IO) monad. Like IO a -> a?

My question originated when using happstack and wanting to get the Text out of ServerPart (Maybe Text) which is returned by (optional $ lookText "domain"). Then I remembered reading that IO monad can't be escaped.

I've read about unsafePerformIO and the reasons why it is bad, but none of those reasons seem to answer my question.

Anton
  • 2,282
  • 26
  • 43
  • What is your actual question? How to solve your problem (which you did not specify precisely enough) or what the rationale is behind the desgn decision that IO cannot be escaped in pure code? – Niklas B. May 28 '14 at 02:24
  • You can't escape from an arbitrary `Monad` because the `Monad` interface doesn't specify a way to do so. The interface doesn't specify a way to do so because the operation isn't part of what a `Monad` is. – John L May 28 '14 at 03:36
  • 1
    Not being "escapable" is *not* a property of monads, rather being "escapable" is not a property of monads. That is to say, you can escape some monads, and some you can't. In Haskell, no computation can have side effects. But obviously a function like `getSystemTime :: IO Int` will be different at different times. If you could have `getSystemTime :: Int` then you could introduce side effects into pure code - which, again, is just not allowed! Banning side effects (aka, purity) is simply a language idiom that gives you a whole bunch of good things in exchange for one very small thing. – user2407038 May 28 '14 at 03:49
  • There's a recent tutorial called ["Inside My World (Ode to Functor and Monad)"](http://blog.jle.im/entry/inside-my-world-ode-to-functor-and-monad) that I feel does a very good job of explaining this. – Luis Casillas May 29 '14 at 00:02
  • Related: https://stackoverflow.com/questions/7154518/unwrapping-a-monad . – atravers Sep 21 '20 at 11:50

2 Answers2

10

Can Monads be escaped from?

Yes. This is very easy with many Monads, such as Maybe, Either a, State, Identity, and more. One of the most common Monads that is escaped from is the function Monad: (->) r. If it weren't possible to turn a function into a value, then Haskell wouldn't have much going for it.

Can IO be escaped from?

Unfortunately, yes. It would be a lot better for beginners if they didn't google around and see that they can technically escape from IO using unsafePerformIO, which as you might have guessed is not safe. It is not meant to be used in normal code, but is rather a backdoor into the runtime system for when you really need it. Primarily, it's used for implementing some lower level libraries like Vector, but also for interfacing with external shared libraries (DLLs). If you're not writing that kind of code, don't use unsafePerformIO. Otherwise, you will end up with code that becomes difficult to reason about and maintain because it bypasses the type system.

How do we escape from other Monads?

It varies from Monad to Monad, but most monad transformers have run-, eval- or exec- methods:

> :m Control.Monad.State
> runState (modify (*10) >> get >>= return . show) 1
("10", 10)
> :type runState (modify (*10) >> get >>= return . show) 1
runState (modify (*10) >> get >>= return . show) 1 :: (String, Int)
> evalState (modify (*10) >> get >>= return . show) 1
"10"
> execState (modify (*10) >> get >>= return . show) 1
10

The Maybe Monad has several ways to escape from it:

> :m Data.Maybe
> maybe "nada" show (Just 2)
"2"
> maybe "nada" show Nothing
"nada"
> fromMaybe 1 (Just 10)
10
> fromMaybe 1 Nothing
1
> fromJust (Just 1)
1
> fromJust Nothing
*** Exception: Maybe.fromJust: Nothing

So as you can see, not all of them are safe to use either.

What does this have to do with Happstack?

I don't know, I haven't used Happstack enough to know about it. However, a quick search led me to this example on their website, which looks pretty applicable to your situation.

bheklilr
  • 53,530
  • 6
  • 107
  • 163
  • There is another way of escaping `Maybe`, and that is by using `case of`. Can I do the same with an `IO` monad? If not, could you point me to a Google search so that I can read up more? – Anton May 28 '14 at 12:28
  • 1
    @Andrey No, you can't pattern match on the `IO` constructors. This is done on purpose, the authors of the compiler don't want you escaping from `IO`. It does take a while to get used to, but the decision to keep `IO` and computations separate is very useful. You can always use pure functions from `IO`, but never the other way around. – bheklilr May 28 '14 at 18:05
5

Let me answer your question with another question: why do you think you can get things out of a monad?

data Dud a = Dud

instance Functor Dud where
  fmap _ _ = Dud

instance Monad Dud where
  return _  = Dud
  Dud >>= _ = Dud

More directly, Monad gives you the ability to create and combine types. Many types exist of this form which may not allow you to actually extract anything at all.

A more meaningful, direct example is the list monad.

returnList :: a -> [a]
returnList a = [a]

bindList :: [a] -> (a -> [b]) -> [b]
bindList as f = concat (map f as)

Lists obviously may contain things, but they also may fail to. There is no function

[a] -> a

which doesn't throw an error when passed [].


Typically, the reason why you want to "get something out" of a monad is that you have an operation like

f :: a -> b

and a monadic value like

m :: [a]

and you'd like to pull that a out of the monad and apply it to your function. As stated above, there's no reason to believe that you can do that.

Instead, you do the opposite—you bring your function into the type!

map f :: [a] -> [b]
J. Abrahamson
  • 72,246
  • 9
  • 135
  • 180