summaryrefslogtreecommitdiff
path: root/FunctorToMaybe.hs
blob: a718010fb25795c98a6777eed30a0bbb9ef5ca69 (plain)
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
---------------------------------------------------------------------------
-- |
-- Module      :  FunctorToMaybe
--
-- Maintainer  :  joe@jerkface.net
-- Stability   :  experimental
--
-- Motivation: When parsing a stream of events, it is often desirable to
-- let certain control events pass-through to the output stream without
-- interrupting the parse.  For example, the conduit package uses 
-- <http://hackage.haskell.org/package/conduit-1.0.13.1/docs/Data-Conduit.html#t:Flush Flush>
-- which adds a special command to a stream and the blaze-builder-conduit 
-- package has <http://hackage.haskell.org/package/blaze-builder-conduit-1.0.0/docs/Data-Conduit-Blaze.html#g:2 conduits> that treat the nullary constructor with special significance.
--
-- But for other intermediary conduits, the nullary @Flush@ constructor may 
-- be noise that they should politely preserve in case it is meaningul downstream.
-- If <http://hackage.haskell.org/package/conduit-1.0.13.1/docs/Data-Conduit.html#t:Flush Flush>
-- implemented the 'FunctorToMaybe' type class, then 'functorToEither' could be used to
-- seperate the noise from the work-product.
--
{-# LANGUAGE CPP #-}
module FunctorToMaybe where

#if MIN_VERSION_base(4,6,0)
#else
import Control.Monad.Instances
#endif

-- | The 'FunctorToMaybe' class genaralizes 'Maybe' in that the
-- there may be multiple null elements. 
--
-- Instances of 'FunctorToMaybe' should satisfy the following laws:
--
-- > functorToMaybe (fmap f g) == fmap f (functorToMaybe g)
--
class Functor g => FunctorToMaybe g where
    functorToMaybe :: g a -> Maybe a


instance FunctorToMaybe Maybe where
    functorToMaybe = id
instance FunctorToMaybe (Either a) where
    functorToMaybe (Right x) = Just x
    functorToMaybe _         = Nothing


-- | 'functorToEither' is a null-preserving cast.
--
-- If @functorToMaybe g == Nothing@, then a casted value is returned with Left.
-- If @functorToMaybe g == Just a@, then @Right a@ is returned.
--
-- Returning to our <http://hackage.haskell.org/package/conduit-1.0.13.1/docs/Data-Conduit.html#t:Flush Flush>
-- example, if we define
--
-- > instance Flush where 
-- >   functorToMaybe Flush     = Nothing
-- >   functorToMaybe (Chunk a) = Just a
--
-- Now stream processors can use 'functorToEither' to transform any nullary constructors while
-- while doing its work to transform the data before forwarding it into 
-- <http://hackage.haskell.org/package/blaze-builder-conduit-1.0.0/docs/Data-Conduit-Blaze.html#v:builderToByteStringFlush builderToByteStringFlush>.
--
functorToEither :: FunctorToMaybe f => f a -> Either (f b) a
functorToEither ga =
    maybe (Left $ uncast ga)
          Right
          (functorToMaybe ga)
 where
    uncast = fmap (error "bad FunctorToMaybe instance")