Ticket #114: ControlMonadMatch.hs

File ControlMonadMatch.hs, 4.4 KB (added by claus, 7 years ago)

main library support for lambda-match composition, etc

Line 
1{-# OPTIONS_GHC -fglasgow-exts -fallow-undecidable-instances #-}
2
3{-
4  support module for working with lambda-matches
5 -}
6
7module ControlMonadMatch where
8
9import Control.Monad
10import Data.Maybe
11
12infixr >|
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
18splice :: Ex (Maybe a) a b c => b -> c
19splice  = ex (maybe (error "no match in splice") id)
20
21-- using (Either String), we can preserve error messages
22spliceE :: Ex (Either String a) a b c => b -> c
23spliceE = ex (either error id)
24
25-- using List
26spliceL :: Ex ([] a) a b c => b -> c
27spliceL = ex (list (error "no match in spliceL") head)
28  where list n c l = if null l then n else c l
29
30allMatches 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
38nomatch :: (Lift (a b) c, MonadPlus a) => c
39nomatch = lift0 mzero
40
41-- explicit match failure, with message
42matchError msg = lift0 $ fail msg
43
44-- default alternative
45fall_through x = lift0 $ return x
46
47-- supply arguments to a match group, without splicing
48expr >| matches = matches expr
49
50-- case x of matches becomes syntactic sugar
51caseOf x matches = x >| (splice matches)
52
53-- the ok from do-notation translation (Section 3.14)
54ok 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)
61newtype Match m a = Match { unMatch :: m a } deriving Show
62
63-- useful when writing out the translation by hand
64match rhs = Match $ return rhs
65
66-- Match m is just a tagged Monad m
67instance 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
76nest :: (Ex (m a) b da dc, Nest (Match m a) b,
77         MatchMonad a m, MatchMonad dc m, MatchMonad da m) 
78     => da -> dc
79nest 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;
86class Nest a b | a -> b, b -> a where
87  nest' :: a -> b
88
89instance Monad m => Nest (Match m (Match m a)) (Match m a) where
90  nest' outer = Match $ ex (>>=unMatch) outer
91
92instance (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)
97class MatchMonad match m | match -> m where annoyingKindConstraint :: match -> m ()
98                                            annoyingKindConstraint = undefined
99instance MatchMonad (Match m a) m
100instance MatchMonad match m => MatchMonad (a->match) m
101
102-- lift (mostly MonadPlus) operations over lambda-match parameters
103class 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
108instance 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
113instance 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)
122class 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
125instance Ex (m a) c (Match m a) c where
126  ex f a = (f (unMatch a))
127
128instance Ex a c da dc => Ex a c (b -> da) (b -> dc) where
129  ex f a = \x->(ex f (a x))