File: Maybe.hs

package info (click to toggle)
haskell-maybet 0.1.2-1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 68 kB
  • sloc: haskell: 49; makefile: 2
file content (164 lines) | stat: -rw-r--r-- 5,006 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}

{- |
Copyright    : 2007 Eric Kidd
License      : BSD3
Stability    : experimental
Portability  : non-portable (multi-parameter type classes, undecidable instances)

The 'MaybeT' monad.  See
<http://www.haskell.org/haskellwiki/New_monads/MaybeT> for more widely-used
version.  Our 'Functor' instance and our implementation of '>>=' are
borrowed from there.

[Computation type:] Computations which may fail or return nothing.

[Binding strategy:] Failure returns the value 'Nothing', bypassing any
bound functions which follow.  Success returns a value wrapped in 'Just'.

[Useful for:] Building computations from steps which may fail.  No error
information is returned.  (If error information is required, see
'Control.Monad.Error'.)

-}

module Control.Monad.Maybe (
  MaybeT(..)
  -- * Limitations
  -- $Limitations

  -- * Example
  -- $MaybeExample
  ) where

import Control.Monad()
import Control.Monad.Trans()
import Control.Monad.Cont
import Control.Monad.Fix()
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Writer

-- | A monad transformer which adds Maybe semantics to an existing monad.
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

instance (Functor m) => Functor (MaybeT m) where
  fmap f = MaybeT . fmap (fmap f) . runMaybeT

instance (Monad m) => Monad (MaybeT m) where
  fail _ = MaybeT (return Nothing)
  return = lift . return
  x >>= f = MaybeT (runMaybeT x >>= maybe (return Nothing) (runMaybeT . f))

instance (Monad m) => MonadPlus (MaybeT m) where
  mzero = MaybeT (return Nothing)
  mplus x y = MaybeT $ do v <- runMaybeT x
                          case v of
                            Nothing -> runMaybeT y
                            Just _  -> return v


instance MonadTrans MaybeT where
  lift x = MaybeT (liftM Just x)

instance (MonadCont m) => MonadCont (MaybeT m) where
  -- Again, I hope this is correct.
  callCC f = MaybeT (callCC (\c -> runMaybeT (f (wrap c))))
    where wrap :: (Maybe a -> m (Maybe b)) -> a -> MaybeT m b
          wrap c = MaybeT . c . Just

-- MonadError: MonadError has fairly weird semantics when lifted by MaybeT,
-- so let's skip it for now.

instance (MonadIO m) => MonadIO (MaybeT m) where
  liftIO = lift . liftIO

instance (MonadFix m) => MonadFix (MaybeT m) where
  -- I hope this is correct.  At a minimum, it typechecks.
  mfix f = MaybeT (mfix (maybe (return Nothing) (runMaybeT . f)))

-- MonadList: Not implemented.

-- MonadPlus: Ambiguous.  See note in introduction.

-- Requires -fallow-undecidable-instances.
instance (MonadReader r m) => MonadReader r (MaybeT m) where
  ask = lift ask
  local f m = MaybeT (local f (runMaybeT m))

-- MonadRWS: Not implemented.

-- Taken from http://www.haskell.org/haskellwiki/New_monads/MaybeT .
instance MonadState s m => MonadState s (MaybeT m) where
  get = lift get
  put = lift . put

-- Requires -fallow-undecidable-instances.
instance (MonadWriter w m) => MonadWriter w (MaybeT m) where
  tell = lift . tell
  listen m = MaybeT (listen (runMaybeT m) >>= (return . liftMaybe))
    where liftMaybe (Nothing, _) = Nothing
          liftMaybe (Just x,  w) = Just (x,w)
  -- I'm not sure this is useful, but it's the best I can do:
  pass m = MaybeT (runMaybeT m >>= maybe (return Nothing)
                                         (liftM Just . pass . return))

{- $Limitations

The instance @MonadPlus@ is not provided, because it has ambiguous
semantics.  It could refer to either

>instance MonadPlus m => MonadPlus (MaybeT m)

...lifting the semantics of an underlying 'MaybeT' monad, or

>instance MonadPlus (MaybeT m)

...with semantics similar to @MonadPlus Maybe@.

-}

{- $MaybeExample

Here is an example that shows how to use 'MaybeT' to propagate an
end-of-file condition in the IO monad.  In the example below, both
@maybeReadLine@ and @failIfQuit@ may cause a failure, which will propagate
out to @main@ without further intervention.

>import System.Console.Readline
>import Data.Maybe
>import Control.Monad
>import Control.Monad.Trans
>import Control.Monad.Maybe
>
>-- 'MaybeIO' is the type of computations which do IO, and which may fail.
>type MaybeIO = MaybeT IO
>
>-- 'readline' already has type 'String -> IO (Maybe String)'; we just need
>-- to wrap it.
>maybeReadLine :: String -> MaybeIO String
>maybeReadLine prompt = MaybeT (readline prompt)
>
>-- Fail if 'str' equals "quit".
>failIfQuit :: (Monad m) => String -> m ()
>failIfQuit str = when (str == "quit") (fail "Quitting")
>
>-- This task may fail in several places.  Try typing Control-D or "quit" at
>-- any prompt.
>concatTwoInputs :: MaybeIO ()
>concatTwoInputs = do
>  s1 <- maybeReadLine "String 1> "
>  failIfQuit s1
>  s2 <- maybeReadLine "String 2> "
>  failIfQuit s2
>  liftIO (putStrLn ("Concatenated: " ++ s1 ++ s2))
>
>-- Loop until failure.
>main :: IO ()
>main = do
>  result <- runMaybeT concatTwoInputs
>  if isNothing result
>    then putStrLn "Bye!"
>    else main

-}