How to extract value from monadic action
Solution 1:
A monad only supplies two functions:
return :: Monad m => a -> m a
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Both of these return something of type m a
, so there is no way to combine these in any way to get a function of type Monad m => m a -> a
. To do that, you'll need more than these two functions, so you need to know more about m
than that it's a monad.
For example, the Identity
monad has runIdentity :: Identity a -> a
, and several monads have similar functions, but there is no way to provide it generically. In fact, the inability to "escape" from the monad is essential for monads like IO
.
Solution 2:
There is probably a better answer than this, but one way to see why you cannot have a type (Monad m) => m a -> a
is to consider a null monad:
data Null a = Null
instance Monad Null where
return a = Null
ma >>= f = Null
Now (Monad m) => m a -> a
means Null a -> a
, ie getting something out of nothing. You can't do that.
Solution 3:
This doesn't exist because Monad
is a pattern for composition, not a pattern for decomposition. You can always put more pieces together with the interface it defines. It doesn't say a thing about taking anything apart.
Asking why you can't take something out is like asking why Java's Iterator
interface doesn't contain a method for adding elements to what it's iterating over. It's just not what the Iterator
interface is for.
And your arguments about specific types having a kind of extract function follows in the exact same way. Some particular implementation of Iterator
might have an add
function. But since it's not what Iterator
s are for, the presence that method on some particular instance is irrelevant.
And the presence of fromJust
is just as irrelevant. It's not part of the behavior Monad
is intended to describe. Others have given lots of examples of types where there is no value for extract
to work on. But those types still support the intended semantics of Monad
. This is important. It means that Monad
is a more general interface than you are giving it credit for.
Solution 4:
Suppose there was such a function:
extract :: Monad m => m a -> a
Now you could write a "function" like this:
appendLine :: String -> String
appendLine str = str ++ extract getLine
Unless the extract
function was guaranteed never to terminate, this would violate referential transparency, because the result of appendLine "foo"
would (a) depend on something other than "foo"
, (b) evaluate to different values when evaluated in different contexts.
Or in simpler words, if there was an actually useful extract
operation Haskell would not be purely functional.