| 1 | {-# OPTIONS_GHC -fglasgow-exts -fallow-undecidable-instances #-} |
|---|
| 2 | |
|---|
| 3 | {- |
|---|
| 4 | support module for working with lambda-matches |
|---|
| 5 | -} |
|---|
| 6 | |
|---|
| 7 | module ControlMonadMatch where |
|---|
| 8 | |
|---|
| 9 | import Control.Monad |
|---|
| 10 | import Data.Maybe |
|---|
| 11 | |
|---|
| 12 | infixr >| |
|---|
| 13 | |
|---|
| 14 | -- extract first successful match if any, splicing match monad |
|---|
| 15 | -- into pure expressions (so: splice (|..->e) = \..->e) |
|---|
| 16 | |
|---|
| 17 | -- we use Maybe as the default matching monad |
|---|
| 18 | splice :: Ex (Maybe a) a b c => b -> c |
|---|
| 19 | splice = ex (maybe (error "no match in splice") id) |
|---|
| 20 | |
|---|
| 21 | -- using (Either String), we can preserve error messages |
|---|
| 22 | spliceE :: Ex (Either String a) a b c => b -> c |
|---|
| 23 | spliceE = ex (either error id) |
|---|
| 24 | |
|---|
| 25 | -- using List |
|---|
| 26 | spliceL :: Ex ([] a) a b c => b -> c |
|---|
| 27 | spliceL = ex (list (error "no match in spliceL") head) |
|---|
| 28 | where list n c l = if null l then n else c l |
|---|
| 29 | |
|---|
| 30 | allMatches matches = ex id matches |
|---|
| 31 | |
|---|
| 32 | -- compose two lambda-match groups, so that match failure |
|---|
| 33 | -- in the first group falls through into the second group |
|---|
| 34 | (+++) :: (Lift (a b) c, MonadPlus a) => c -> c -> c |
|---|
| 35 | (+++) = lift2 mplus |
|---|
| 36 | |
|---|
| 37 | -- explicit match failure |
|---|
| 38 | nomatch :: (Lift (a b) c, MonadPlus a) => c |
|---|
| 39 | nomatch = lift0 mzero |
|---|
| 40 | |
|---|
| 41 | -- explicit match failure, with message |
|---|
| 42 | matchError msg = lift0 $ fail msg |
|---|
| 43 | |
|---|
| 44 | -- default alternative |
|---|
| 45 | fall_through x = lift0 $ return x |
|---|
| 46 | |
|---|
| 47 | -- supply arguments to a match group, without splicing |
|---|
| 48 | expr >| matches = matches expr |
|---|
| 49 | |
|---|
| 50 | -- case x of matches becomes syntactic sugar |
|---|
| 51 | caseOf x matches = x >| (splice matches) |
|---|
| 52 | |
|---|
| 53 | -- the ok from do-notation translation (Section 3.14) |
|---|
| 54 | ok match = ex (>>=id) match |
|---|
| 55 | |
|---|
| 56 | -- we wrap lambda-match bodies, to steer lifting |
|---|
| 57 | -- (as we do not want to pin down the inner match monad |
|---|
| 58 | -- too early, we'd otherwise have too many ambiguities; |
|---|
| 59 | -- also, functions can be monads, so we need a marker |
|---|
| 60 | -- to know when lifting has reached the match body) |
|---|
| 61 | newtype Match m a = Match { unMatch :: m a } deriving Show |
|---|
| 62 | |
|---|
| 63 | -- useful when writing out the translation by hand |
|---|
| 64 | match rhs = Match $ return rhs |
|---|
| 65 | |
|---|
| 66 | -- Match m is just a tagged Monad m |
|---|
| 67 | instance Monad m => Monad (Match m) where |
|---|
| 68 | fail = Match . fail |
|---|
| 69 | return = Match . return |
|---|
| 70 | a >>= b = Match $ unMatch a >>= (unMatch . b) |
|---|
| 71 | |
|---|
| 72 | -- join nested matches, if MatchMonads are the same |
|---|
| 73 | -- the parameter prefix of the outer match remains, |
|---|
| 74 | -- but may be extended (via nest') if the inner match |
|---|
| 75 | -- has parameters as well |
|---|
| 76 | nest :: (Ex (m a) b da dc, Nest (Match m a) b, |
|---|
| 77 | MatchMonad a m, MatchMonad dc m, MatchMonad da m) |
|---|
| 78 | => da -> dc |
|---|
| 79 | nest match = ex (nest' . Match) match |
|---|
| 80 | |
|---|
| 81 | -- auxiliary |
|---|
| 82 | -- base case: join the tagged match monads themselves |
|---|
| 83 | -- function case: shift parameters from inner to outer match |
|---|
| 84 | -- using eta-extension to promise functions where |
|---|
| 85 | -- there used to be matches possibly returning functions; |
|---|
| 86 | class Nest a b | a -> b, b -> a where |
|---|
| 87 | nest' :: a -> b |
|---|
| 88 | |
|---|
| 89 | instance Monad m => Nest (Match m (Match m a)) (Match m a) where |
|---|
| 90 | nest' outer = Match $ ex (>>=unMatch) outer |
|---|
| 91 | |
|---|
| 92 | instance (Nest (Match m b) c, Monad m) => Nest (Match m (a -> b)) (a -> c) where |
|---|
| 93 | nest' outer = (\a-> nest' $ Match $ ex (\inner-> inner >>= \f-> return $ f a) outer) |
|---|
| 94 | |
|---|
| 95 | -- extract match monads from possibly parameterised matches |
|---|
| 96 | -- (so that we can relate them, eg, in nest) |
|---|
| 97 | class MatchMonad match m | match -> m where annoyingKindConstraint :: match -> m () |
|---|
| 98 | annoyingKindConstraint = undefined |
|---|
| 99 | instance MatchMonad (Match m a) m |
|---|
| 100 | instance MatchMonad match m => MatchMonad (a->match) m |
|---|
| 101 | |
|---|
| 102 | -- lift (mostly MonadPlus) operations over lambda-match parameters |
|---|
| 103 | class Lift a d | d -> a where |
|---|
| 104 | lift0 :: a -> d |
|---|
| 105 | lift1 :: (a -> a) -> d -> d |
|---|
| 106 | lift2 :: (a -> a -> a) -> d -> d -> d |
|---|
| 107 | |
|---|
| 108 | instance Lift (m a) (Match m a) where |
|---|
| 109 | lift0 c = Match c |
|---|
| 110 | lift1 f a = Match (f (unMatch a)) |
|---|
| 111 | lift2 op a b = Match (op (unMatch a) (unMatch b)) |
|---|
| 112 | |
|---|
| 113 | instance Lift a c => Lift a (b->c) where |
|---|
| 114 | lift0 c = \x-> lift0 c |
|---|
| 115 | lift1 f a = \x-> lift1 f (a x) |
|---|
| 116 | lift2 op a b = \x-> lift2 op (a x) (b x) |
|---|
| 117 | |
|---|
| 118 | -- extract (with function) from inner match monad |
|---|
| 119 | -- (extraction is lifted over lambda-match parameters; |
|---|
| 120 | -- we cannot express all functional dependencies, |
|---|
| 121 | -- because the inner c could be a function type) |
|---|
| 122 | class Ex a c da dc | da -> a, dc da -> c, da c -> dc {- , dc a c -> da -} where |
|---|
| 123 | ex :: (a -> c) -> da -> dc |
|---|
| 124 | |
|---|
| 125 | instance Ex (m a) c (Match m a) c where |
|---|
| 126 | ex f a = (f (unMatch a)) |
|---|
| 127 | |
|---|
| 128 | instance Ex a c da dc => Ex a c (b -> da) (b -> dc) where |
|---|
| 129 | ex f a = \x->(ex f (a x)) |
|---|