|
38 | 38 | %% Structural rules for types of kind `*` |
39 | 39 | struct_rule(class(ClassName, class_arg(_, kind(*)), _), Rule) :- |
40 | 40 | member(Rule, [ |
41 | | - (rule(ClassName, opaque(_, kind(*), _)) :- true), |
42 | | - (rule(ClassName, ty_ref(unit)) :- true), |
43 | | - (rule(ClassName, ty_ref(void)) :- true), |
44 | | - (rule(ClassName, ty_app(ty_app(ty_ref(prod), A), B)) :- |
| 41 | + (goal(ClassName, opaque(_, kind(*), _)) :- true), |
| 42 | + (goal(ClassName, ty_ref(unit)) :- true), |
| 43 | + (goal(ClassName, ty_ref(void)) :- true), |
| 44 | + (goal(ClassName, ty_app(ty_app(ty_ref(prod), A), B)) :- |
45 | 45 | ( |
46 | | - rule(ClassName, A), |
47 | | - rule(ClassName, B) |
| 46 | + goal(ClassName, A), |
| 47 | + goal(ClassName, B) |
48 | 48 | )), |
49 | | - (rule(ClassName, ty_app(ty_app(ty_ref(either), A), B)) :- |
| 49 | + (goal(ClassName, ty_app(ty_app(ty_ref(either), A), B)) :- |
50 | 50 | ( |
51 | | - rule(ClassName, A), |
52 | | - rule(ClassName, B) |
| 51 | + goal(ClassName, A), |
| 52 | + goal(ClassName, B) |
53 | 53 | )) |
54 | 54 | ]). |
55 | 55 |
|
| 56 | +conj(Goal, (Goal, Conj), Conj). |
| 57 | + |
| 58 | +superclass_goal(Ty, Cl_, Conj) :- |
| 59 | + copy_term(Cl_, Cl), |
| 60 | + class(_ClassName, class_arg(Ty, _K), ClassSups) = Cl, |
| 61 | + findall(R, ( |
| 62 | + member(Sup_, ClassSups), |
| 63 | + copy_term(Sup_, Sup), |
| 64 | + Sup =.. [SupName, Ty], |
| 65 | + R = goal(SupName, Ty) |
| 66 | + ), |
| 67 | + Rules), |
| 68 | + foldl(conj, Rules, Conj, true). |
| 69 | + |
| 70 | + |
56 | 71 | %% User specifiable `derive` rules (the same for any kind?) |
57 | 72 | %% NOTE(bladyjoker): TyAbs can't be derived for non `*` kinds. |
58 | | -derive_rule(ty_ref(RefName), class(ClassName, _, _), Rule) :- |
| 73 | +derive_rule(ty_ref(RefName), class(ClassName, ClassArgs, ClassSups), Rule) :- |
59 | 74 | ty_def(RefName, Ty), |
60 | | - Rule = (rule(ClassName, ty_ref(RefName)) :- rule(ClassName, Ty)). |
| 75 | + superclass_goal(ty_ref(RefName), class(ClassName, ClassArgs, ClassSups), SupGoals), |
| 76 | + Rule = (goal(ClassName, ty_ref(RefName)) :- goal(ClassName, Ty), SupGoals). |
61 | 77 |
|
62 | | -derive_rule(ty_app(F, A), class(ClassName, _, _), Rule) :- |
| 78 | +derive_rule(ty_app(F, A), class(ClassName, ClassArgs, ClassSups), Rule) :- |
63 | 79 | apply(F, A, Res), |
64 | | - Rule = (rule(ClassName, ty_app(F, A)) :- rule(ClassName, Res)). |
| 80 | + superclass_goal(ty_app(F, A), class(ClassName, ClassArgs, ClassSups), SupGoals), |
| 81 | + Rule = (goal(ClassName, ty_app(F, A)) :- goal(ClassName, Res), SupGoals). |
65 | 82 |
|
66 | 83 | %% Experimental structural rules for types of kind * -> * |
67 | 84 | % Haskell: Functor Deriving https://mail.haskell.org/pipermail/haskell-prime/2007-March/002137.html |
|
82 | 99 |
|
83 | 100 |
|
84 | 101 |
|
85 | | -class_def(eq, class_arg(a, kind(*)), []). |
86 | | -class_def(ord, class_arg(a, kind(*)), [eq(a)]). |
87 | | -class_def(json, class_arg(a, kind(*)), []). |
88 | | -class_def(functor, class_arg(a, kind(arr(*, *))), []). |
| 102 | +class_def(eq, class_arg(_A, kind(*)), []). |
| 103 | +class_def(ord, class_arg(A, kind(*)), [eq(A)]). |
| 104 | +class_def(json, class_arg(_A, kind(*)), []). |
| 105 | +class_def(functor, class_arg(_A, kind(arr(*, *))), []). |
89 | 106 |
|
90 | 107 |
|
91 | 108 | derive(Tys, CName, StructRules, UserRules) :- |
|
114 | 131 | solve(StructRules, UserRules, Goal) :- |
115 | 132 | Goal =.. [ClassName, Ty], |
116 | 133 | append(StructRules, UserRules, Rules), |
117 | | - eval_rule(Rules, [], rule(ClassName, Ty)) -> true; |
118 | | - ( |
119 | | - print_message(error, rule_failed(Goal)), |
120 | | - fail |
121 | | - ). |
| 134 | + solve_goal(Rules, [], goal(ClassName, Ty)) ->( true; |
| 135 | + print_message(error, goal_failed(goal(ClassName, Ty))), |
| 136 | + fail |
| 137 | + ). |
122 | 138 |
|
123 | | -eval_rule(_, _, true) :- |
124 | | - print_message(informational, rule_true). |
| 139 | +solve_goal(_, Trace, true) :- |
| 140 | + print_message(informational, goal_true(Trace)). |
125 | 141 |
|
126 | | -eval_rule(Rules, Trace, (RL,RR)) :- |
127 | | - eval_rule(Rules, Trace, RL), |
128 | | - eval_rule(Rules, Trace, RR). |
| 142 | +solve_goal(Rules, Trace, (GL,GR)) :- |
| 143 | + solve_goal(Rules, Trace, GL), |
| 144 | + solve_goal(Rules, Trace, GR). |
129 | 145 |
|
130 | | -eval_rule(Rules, Trace, rule(ClassName, Ty)) :- |
131 | | - var(Ty) -> print_message(informational, rule_ok(rule(ClassName, Ty))), true; |
132 | | - first(rule(ClassName, Ty), Trace) -> print_message(informational, rule_ok_cycle(rule(ClassName, Ty))), true; |
| 146 | +solve_goal(Rules, Trace, goal(ClassName, Ty)) :- |
| 147 | + var(Ty) -> print_message(informational, goal_ok(goal(ClassName, Ty), Trace)), true; |
| 148 | + check_cycle(Trace, goal(ClassName, Ty)) -> true; |
133 | 149 | ( |
134 | | - print_message(informational, lookup(rule(ClassName, Ty))), |
| 150 | + print_message(informational, |
| 151 | + lookup(goal(ClassName, Ty), Trace) |
| 152 | + ), |
135 | 153 | copy_term(Rules, Rules_), %% WARN(bladyjoker): Without this, Rules get unified and instantiated leading to a cycle and just wrong. |
136 | | - first((rule(ClassName, Ty) :- RuleBody), Rules_) -> ( |
137 | | - print_message(informational, trying(rule(ClassName, Ty))), |
138 | | - eval_rule(Rules, [rule(ClassName, Ty)|Trace], RuleBody), |
139 | | - print_message(informational, rule_ok(rule(ClassName, Ty))) |
| 154 | + first((goal(ClassName, Ty) :- RuleBody), Rules_) -> ( |
| 155 | + print_message(informational, |
| 156 | + running(goal(ClassName, Ty), Trace) |
| 157 | + ), |
| 158 | + solve_goal(Rules, [goal(ClassName, Ty)|Trace], RuleBody), |
| 159 | + print_message(informational, |
| 160 | + goal_ok(goal(ClassName, Ty), Trace) |
| 161 | + ) |
140 | 162 | ); |
141 | 163 | ( |
142 | | - print_message(error, missing_rule(rule(ClassName, Ty), Trace)), |
| 164 | + print_message(error, missing_rule(goal(ClassName, Ty), Trace)), |
143 | 165 | fail |
144 | 166 | ) |
145 | 167 | ). |
146 | 168 |
|
| 169 | +check_cycle(Trace, Goal) :- |
| 170 | + copy_term(Trace, Trace_), %% WARN(bladyjoker): Without this, Trace gets unified and instantiated. |
| 171 | + print_message(informational, checking_cycle(Goal, Trace)), |
| 172 | + (member(TracedGoal, Trace_), TracedGoal =@= Goal) -> print_message(informational, goal_ok_cycle(Goal, Trace)); fail. |
| 173 | + |
| 174 | + |
147 | 175 | :- multifile prolog:message//1. |
148 | 176 |
|
149 | | -prolog:message(wrong_kind(Ty, got(Got), wanted(Want))) --> [ '~w is of kind ~w but wanted kind ~w'-[Ty, Got, Want]]. |
150 | | -prolog:message(normalization_failed(_, Ty)) --> [ 'Normalizing ~w failed'-[Ty]]. |
151 | | -prolog:message(lookup(rule(ClassName, Ty))) --> [ 'Looking up rule ~w ~w'-[ClassName, Ty]]. |
152 | | -prolog:message(trying(rule(ClassName, Ty))) --> [ 'Trying rule ~w ~w'-[ClassName, Ty]]. |
153 | | -prolog:message(rule_ok(rule(ClassName, Ty))) --> [ 'Done with rule ~w ~w'-[ClassName, Ty]]. |
154 | | -prolog:message(rule_ok_cycle(rule(ClassName, Ty))) --> [ 'Done with rule because cycle ~w ~w'-[ClassName, Ty]]. |
155 | | -prolog:message(rule_true) --> [ 'Done because bottom']. |
156 | | -prolog:message(missing_rule(rule(ClassName, Ty), _)) --> [ 'Missing rule ~w ~w'-[ClassName, Ty]]. |
157 | | -prolog:message(rule_failed(rule(ClassName, Ty))) --> [ 'Failed rule ~w ~w'-[ClassName, Ty]]. |
| 177 | +trace_to_indentation([], ""). |
| 178 | +trace_to_indentation([_|Xs], I) :- |
| 179 | + trace_to_indentation(Xs, Is), |
| 180 | + string_concat(".", Is, I). |
| 181 | + |
| 182 | +prolog:message(checking_cycle(G, Trace)) --> { |
| 183 | + trace_to_indentation(Trace, I), |
| 184 | + pretty_goal(G, PG) |
| 185 | + }, [ |
| 186 | + '~w ~w Checking cycle for for goal'-[I, PG] |
| 187 | + ]. |
| 188 | + |
| 189 | +prolog:message(lookup(G, Trace)) --> { |
| 190 | + trace_to_indentation(Trace, I), |
| 191 | + pretty_goal(G, PG) |
| 192 | + }, [ |
| 193 | + '~w ~w Looking up rule for goal'-[I, PG] |
| 194 | + ]. |
| 195 | +prolog:message(running(G, Trace)) --> { |
| 196 | + trace_to_indentation(Trace, I), |
| 197 | + pretty_goal(G, PG) |
| 198 | + }, [ |
| 199 | + '~w ~w Running goal'-[I, PG] |
| 200 | + ]. |
| 201 | +prolog:message(goal_ok(G, Trace)) --> { |
| 202 | + trace_to_indentation(Trace, I), |
| 203 | + pretty_goal(G, PG) |
| 204 | + }, [ |
| 205 | + '~w ~w Done with goal'-[I, PG] |
| 206 | + ]. |
| 207 | +prolog:message(goal_ok_cycle(G, Trace)) --> { |
| 208 | + trace_to_indentation(Trace, I), |
| 209 | + pretty_goal(G, PG), |
| 210 | + pretty_trace(Trace, PTrace) |
| 211 | + }, [ |
| 212 | + '~w ~w Done with goal because cycle ~w '-[I, PG, PTrace] |
| 213 | + ]. |
| 214 | +prolog:message(goal_true(Trace)) --> { trace_to_indentation(Trace, I) }, [ '~w Done because bottom'-[I]]. |
| 215 | +prolog:message(missing_rule(G, Trace)) --> { |
| 216 | + trace_to_indentation(Trace, I), |
| 217 | + pretty_goal(G, PG) |
| 218 | + }, [ |
| 219 | + '~w ~w Missing rule for goal'-[I, PG] |
| 220 | + ]. |
| 221 | +prolog:message(goal_failed(G)) --> {pretty_goal(G, PG)}, ['~w Failed goal'-[PG]]. |
| 222 | + |
| 223 | +%% Pretty represenationts |
| 224 | +%% ?- pretty_ty(ty_app(ty_app(ty_ref(either), ty_ref(int)), B), P). |
| 225 | +%% P = either(int, B). |
| 226 | +pretty_ty(TyVar, TyVar) :- |
| 227 | + var(TyVar). |
| 228 | +pretty_ty(opaque(N, _, _), P) :- |
| 229 | + atom_concat('_', N, OpaqueN), |
| 230 | + P =.. [OpaqueN]. |
| 231 | +pretty_ty(ty_ref(RefName), P) :- |
| 232 | + P =.. [RefName]. |
| 233 | +pretty_ty(ty_app(TyF, TyA), P) :- |
| 234 | + (var(TyF) -> PTyF = TyF; pretty_ty(TyF, PTyF)), |
| 235 | + (var(TyA) -> PTyA = TyA; pretty_ty(TyA, PTyA)), |
| 236 | + PTyF =.. [N|Args], |
| 237 | + append(Args, [PTyA], PArgs), |
| 238 | + P =.. [N|PArgs]. |
| 239 | + |
| 240 | +pretty_goal(goal(ClassName, Ty), P) :- |
| 241 | + pretty_ty(Ty, PTy), |
| 242 | + P =.. [ClassName, PTy]. |
| 243 | + |
| 244 | +pretty_trace(Trace, PTrace) :- |
| 245 | + findall(P, (member(R, Trace), pretty_goal(R, P)), PTrace). |
158 | 246 |
|
159 | 247 | :- begin_tests(class_check). |
160 | 248 |
|
|
203 | 291 | ], eq, S, U), |
204 | 292 | solve(S, U, eq(ty_ref(recfoo))). |
205 | 293 |
|
206 | | -test("should_succeed: derive Maybe (Maybe Int8))", [ ]) :- |
| 294 | +test("should_succeed: derive Eq Maybe (Maybe Int8))", [ ]) :- |
207 | 295 | derive([ |
208 | 296 | ty_ref(int8), |
209 | 297 | ty_app(ty_ref(maybe), _A) |
210 | 298 | ], eq, S, U), |
211 | 299 | solve(S, U, eq(ty_app(ty_ref(maybe), ty_app(ty_ref(maybe), ty_ref(int8))))). |
212 | 300 |
|
213 | | -test("should_succeed: derive Maybe (Maybe a)", [ ]) :- |
| 301 | +test("should_succeed: derive Eq Maybe (Maybe a)", [ ]) :- |
214 | 302 | derive([ |
215 | 303 | ty_ref(int8), |
216 | 304 | ty_app(ty_ref(maybe), _A) |
217 | 305 | ], eq, S, U), |
218 | 306 | solve(S, U, eq(ty_app(ty_ref(maybe), ty_app(ty_ref(maybe), _B)))). |
219 | 307 |
|
| 308 | +test("should_fail: derive Ord (Maybe Int)", [ fail ]) :- |
| 309 | + derive([ |
| 310 | + ty_app(ty_ref(maybe), _A) |
| 311 | + ], ord, S, U), |
| 312 | + solve(S, U, ord(ty_app(ty_ref(maybe), ty_app(ty_ref(maybe), ty_ref(int8))))). |
| 313 | + |
| 314 | +test("should_fail: derive Ord (Maybe Int)", [ fail ]) :- |
| 315 | + derive([ |
| 316 | + ty_ref(int8), |
| 317 | + ty_app(ty_ref(maybe), _A) |
| 318 | + ], ord, S, U), |
| 319 | + solve(S, U, ord(ty_app(ty_ref(maybe), ty_app(ty_ref(maybe), ty_ref(int8))))). |
| 320 | + |
| 321 | +test("should_succeed: derive Ord (Maybe a)", [ fail ]) :- |
| 322 | + derive([ |
| 323 | + ty_ref(int8) |
| 324 | + ], eq, EqS, EqU), |
| 325 | + derive([ |
| 326 | + ty_ref(int8), |
| 327 | + ty_app(ty_ref(maybe), __A) |
| 328 | + ], ord, OrdS, OrdU), |
| 329 | + append(EqS, OrdS, S), |
| 330 | + append(EqU, OrdU, U), |
| 331 | + solve(S, U, ord(ty_app(ty_ref(maybe), ty_app(ty_ref(maybe), ty_ref(int8))))). |
| 332 | + |
| 333 | +test("should_succeed: derive Ord (Maybe a)", [ ]) :- |
| 334 | + derive([ |
| 335 | + ty_ref(int8), |
| 336 | + ty_app(ty_ref(maybe), _A) |
| 337 | + ], eq, EqS, EqU), |
| 338 | + derive([ |
| 339 | + ty_ref(int8), |
| 340 | + ty_app(ty_ref(maybe), __A) |
| 341 | + ], ord, OrdS, OrdU), |
| 342 | + append(EqS, OrdS, S), |
| 343 | + append(EqU, OrdU, U), |
| 344 | + solve(S, U, ord(ty_app(ty_ref(maybe), ty_app(ty_ref(maybe), ty_ref(int8))))). |
| 345 | + |
| 346 | +test("should_fails: Eq List a => Eq List a", [ ]) :- |
| 347 | + solve([ |
| 348 | + ( |
| 349 | + goal(eq, ty_app(ty_ref(list), A)) :- |
| 350 | + (goal(eq, ty_app(ty_ref(list), A)),true) |
| 351 | + ) |
| 352 | + ], |
| 353 | + [], |
| 354 | + eq(ty_app(ty_ref(list), _B)) |
| 355 | + ). |
| 356 | + |
220 | 357 | :- end_tests(class_check). |
0 commit comments