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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
|
module Instance (instanceWith, instanceOf, msg) where
import List (find, nub)
import Maybe (isJust)
import Text.Pretty (pPrint, ($$), text)
import State ( State, (<$>), (<*>), (>+), (>+=), concatMapS, getsS
, mapS, modifyS, returnS, runState)
import Utils (disjoint, sameLength)
import FlatCurry.Types
import FlatCurryGoodies ( branchExprs, branchPats, samePattern, freeVars
, isConstrTerm, maxVar, patVars, maximumVarIndex)
import FlatCurryPretty (ppExp, indent)
import Normalization (eqRen, renameExprSubst)
import Output (assert)
import Subst ( Subst, ppSubst, emptySubst, combine, compose, dom
, restrict, singleSubst, subst, toSubst, varSubst
, isDetSubst )
strictInstanceOf :: Expr -> Expr -> Bool
strictInstanceOf e1 e2 = instanceOf e1 e2 && not (instanceOf e2 e1)
detInstanceOf :: Expr -> Expr -> Bool
detInstanceOf e1 e2 = case instanceWith e1 e2 of
Nothing -> False
Just s -> isDetSubst e2 s
instanceOf :: Expr -> Expr -> Bool
instanceOf e1 e2 = isJust (instanceWith e1 e2)
instanceWith :: Expr -> Expr -> Maybe Subst
instanceWith e1 e2 = case instance' re1 re2 of
Nothing -> Nothing
Just s -> let s' = restrict (dom s2) $ compose s1 $ compose s s2
in assert (subst s re2 `eqRen` e1) "error in instance"
$ Just s'
where
(re1, r1) = renameExprSubst e1
(re2, r2) = renameExprSubst e2
s1 = toSubst [(x, Var y) | (y, x) <- r1]
s2 = toSubst [(x, Var y) | (x, y) <- r2]
instance' :: Expr -> Expr -> Maybe Subst
instance' ex1 ex2 = case (ex1, ex2) of
(Var x, Var y)
| x == y -> Just $ if isUnboundVariable y
then singleSubst y ex1
else emptySubst
(_ , Var y)
| all isUnboundVariable
(y : freeVars ex1) -> Just (singleSubst y ex1)
(Lit x, Lit y)
| x == y -> Just emptySubst
(Comb c1 f1 es1, Comb c2 f2 es2)
| c1 == c2 && f1 == f2
&& sameLength es1 es2 -> instanceL es1 es2
(Let ds1 e1, Let ds2 e2)
| sameLength ds1 ds2 -> let (vs1, bs1) = unzip ds1
(vs2, bs2) = unzip ds2
in instanceL
(map (varSubst vs1 vs2) (e1 : bs1))
(e2 : bs2)
(Free vs1 e1, Free vs2 e2)
| sameLength vs1 vs2 -> instance' (varSubst vs1 vs2 e1) e2
(Or e1 f1, Or e2 f2) -> instanceL [e1,f1] [e2,f2]
(Case c1 e1 b1, Case c2 e2 b2)
| c1 == c2 && samePattern b1 b2 -> foldl union (instance' e1 e2)
(zipWith instanceBranch b1 b2)
(Typed e1 ty1, Typed e2 ty2)
| ty1 == ty2 -> instance' e1 e2
_ -> Nothing
isUnboundVariable :: VarIndex -> Bool
isUnboundVariable v = v < 0
instanceL :: [Expr] -> [Expr] -> Maybe Subst
instanceL es1 es2 = foldl union (Just emptySubst) (zipWith instance' es1 es2)
instanceBranch :: BranchExpr -> BranchExpr -> Maybe Subst
instanceBranch (Branch p1 e1) (Branch p2 e2)
= instance' (varSubst (patVars p1) (patVars p2) e1) e2
union :: Maybe Subst -> Maybe Subst -> Maybe Subst
union Nothing _ = Nothing
union (Just _ ) Nothing = Nothing
union (Just s1) (Just s2) = Subst.combine s1 s2
msg :: Expr -> Expr -> (Expr, Subst, Subst)
msg e1 e2
= let (g, state) = runState (msg' (e1, e2))
(initState (maximumVarIndex [e1, e2] + 1))
l = msgSubst state
fv = freeVars g
s1 = toSubst [ (v, e) | (v, e, _) <- l, v `elem` fv]
s2 = toSubst [ (v, e) | (v, _, e) <- l, v `elem` fv]
in assertInstance e1 g s1 $ assertInstance e2 g s2 $ (g, s1, s2)
where
assertInstance e g s x = assert (eqRen (subst s g) e) (pPrint doc) x
where doc = indent (text "Expression" $$ ppExp e)
$$ indent (text "is no instance of" $$ ppExp g)
$$ indent (text "with substitution" $$ ppSubst s)
type Msg a = State MsgState a
data MsgState = MsgState
{ msgFresh :: VarIndex
, msgSubst :: [(VarIndex, Expr, Expr)]
}
initState :: VarIndex -> MsgState
initState x = MsgState x []
getFresh :: Msg VarIndex
getFresh = getsS msgFresh >+= \i ->
modifyS (\s -> s { msgFresh = i + 1 }) >+
returnS i
newSubst :: Expr -> Expr -> Msg Expr
newSubst e1 e2 = getsS msgSubst >+= \sub ->
getFresh >+= \j ->
modifyS (\s -> s { msgSubst = (j, e1, e2) : sub }) >+
returnS (Var j)
getSubst :: Expr -> Expr -> Msg Expr
getSubst e1 e2
| all isConstrTerm [e1, e2]
= getsS msgSubst >+= \sub ->
case find (\(_, s1, s2) -> e1 == s1 && e2 == s2) sub of
Just (v, _, _) -> returnS (Var v)
Nothing -> newSubst e1 e2
| otherwise = newSubst e1 e2
substsUseLocalVars :: [VarIndex] -> [Expr] -> Msg Bool
substsUseLocalVars vs es =
concatMapS substsFor (nub $ concatMap freeVars es) >+= \es' ->
returnS $ not $ disjoint vs (nub $ concatMap freeVars es')
where
substsFor v = getsS msgSubst >+= \sub ->
case find (\(x, _, _) -> x == v) sub of
Just (_ , e1, e2) -> returnS [e1, e2]
Nothing -> returnS []
msg' :: (Expr, Expr) -> Msg Expr
msg' p@(ex1, ex2) = case p of
(Var x, Var y) | x == y
-> returnS ex1
(Lit x, Lit y) | x == y
-> returnS ex1
(Comb c1 f1 e1, Comb c2 f2 e2) | c1 == c2 && f1 == f2 && sameLength e1 e2
-> Comb c1 f1 <$> mapS msg' (zip e1 e2)
(Let ds1 e1, Let ds2 e2) | sameLength ds1 ds2
-> mapS msgVar (zip vs1 vs2) >+= \vs ->
let es1' = map (varSubst vs1 vs) es1
es2' = map (varSubst vs2 vs) es2 in
mapS msg' (zip es1' es2') >+= \es' ->
msg' (varSubst vs1 vs e1, varSubst vs2 vs e2) >+= \e' ->
substsUseLocalVars vs (e':es') >+= \used ->
if used then getSubst ex1 ex2 else returnS (Let (zip vs es') e')
where (vs1, es1) = unzip ds1
(vs2, es2) = unzip ds2
(Free xs e1, Free ys e2) | sameLength xs ys
-> mapS msgVar (zip xs ys) >+= \zs ->
msg' (varSubst xs zs e1, varSubst ys zs e2) >+= \e' ->
substsUseLocalVars zs [e'] >+= \used ->
if used then getSubst ex1 ex2 else returnS (Free zs e')
(Or x1 y1, Or x2 y2)
-> Or <$> msg' (x1, x2) <*> msg' (y1, y2)
(Case c1 e1 b1, Case c2 e2 b2) | c1 == c2 && samePattern b1 b2
-> msg' (e1, e2) >+= \e ->
mapS msgBranch (zip b1 b2) >+= \bs ->
let vs = nub $ concatMap patVars (branchPats bs) in
substsUseLocalVars vs (branchExprs bs) >+= \used ->
if used then getSubst ex1 ex2 else returnS $ Case c1 e bs
(Typed x1 ty1, Typed x2 ty2)
| ty1 == ty2 -> flip Typed ty1 <$> msg' (x1, x2)
(_ , _ ) -> getSubst ex1 ex2
msgBranch :: (BranchExpr, BranchExpr) -> Msg BranchExpr
msgBranch (Branch p1 e1, Branch p2 e2) = case (p1, p2) of
(LPattern l , LPattern _) -> Branch (LPattern l) <$> msg' (e1, e2)
(Pattern c xs, Pattern _ ys) -> mapS msgVar (zip xs ys) >+= \zs ->
Branch (Pattern c zs) <$>
let e1' = varSubst xs zs e1
e2' = varSubst ys zs e2
in msg' (e1', e2')
_ -> error "Instance.msgBranch"
msgVar :: (VarIndex, VarIndex) -> Msg VarIndex
msgVar (x, y) = if x == y then returnS x else getFresh
|