|
| 1 | +:- use_module(common_defs). |
| 2 | +:- use_module(kind_check). |
| 3 | + |
| 4 | +%% Apply and argument to a type abstraction - assumes Kind validity |
| 5 | +% Beta-reduction Apply an argument to a type abstraction - assumes Kind validity |
| 6 | +apply(opaque(N, kind(arr(_KL, KR)), Cd), _, opaque(N, kind(KR), Cd)). |
| 7 | + |
| 8 | +apply(ty_ref(RefName), A, ResTy) :- |
| 9 | + ty_def(RefName, Ty), |
| 10 | + apply(Ty, A, ResTy). |
| 11 | + |
| 12 | +apply(ty_app(L, R), A, Res) :- |
| 13 | + apply(L, R, ResApp), |
| 14 | + apply(ResApp, A, Res). |
| 15 | + |
| 16 | +apply(ty_abs(ArgName-_, Body), A, Res) :- |
| 17 | + subst(ctx(ArgName-A, []), Body, Res). |
| 18 | + |
| 19 | +subst(ctx(VarName-A, Args), ty_var(VarName), Res) :- |
| 20 | + first(VarName, Args) -> Res = ty_var(VarName); |
| 21 | + Res = A. |
| 22 | + |
| 23 | +subst(ctx(ArgName-_, _), ty_var(VarName), ty_var(VarName)) :- |
| 24 | + ArgName \= VarName. |
| 25 | + |
| 26 | +subst(_, opaque(N, K, Cd), opaque(N, K, Cd)). |
| 27 | + |
| 28 | +subst(_, ty_ref(RefName), ty_ref(RefName)). |
| 29 | + |
| 30 | +subst(ctx(Arg, Args), ty_abs(ArgName-KArg, Body), ty_abs(ArgName-KArg, Res)) :- |
| 31 | + subst(ctx(Arg, [ArgName|Args]), Body, Res). |
| 32 | + |
| 33 | +subst(Ctx, ty_app(TyAbs, TyArg), ty_app(AbsRes, ArgRes)) :- |
| 34 | + subst(Ctx, TyAbs, AbsRes), |
| 35 | + subst(Ctx, TyArg, ArgRes). |
| 36 | + |
| 37 | +%% Sum/Product/Rec get normalized into a canonical form Either/Prod/Void/Unit/Opaque |
| 38 | +%% Structural rules for types of kind `*` |
| 39 | +struct_rule(class(ClassName, class_arg(_, kind(*)), _), Rule) :- |
| 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)) :- |
| 45 | + ( |
| 46 | + rule(ClassName, A), |
| 47 | + rule(ClassName, B) |
| 48 | + )), |
| 49 | + (rule(ClassName, ty_app(ty_app(ty_ref(either), A), B)) :- |
| 50 | + ( |
| 51 | + rule(ClassName, A), |
| 52 | + rule(ClassName, B) |
| 53 | + )) |
| 54 | + ]). |
| 55 | + |
| 56 | +%% User specifiable `derive` rules (the same for any kind?) |
| 57 | +%% NOTE(bladyjoker): TyAbs can't be derived for non `*` kinds. |
| 58 | +derive_rule(ty_ref(RefName), class(ClassName, _, _), Rule) :- |
| 59 | + ty_def(RefName, Ty), |
| 60 | + Rule = (rule(ClassName, ty_ref(RefName)) :- rule(ClassName, Ty)). |
| 61 | + |
| 62 | +derive_rule(ty_app(F, A), class(ClassName, _, _), Rule) :- |
| 63 | + apply(F, A, Res), |
| 64 | + Rule = (rule(ClassName, ty_app(F, A)) :- rule(ClassName, Res)). |
| 65 | + |
| 66 | +%% Experimental structural rules for types of kind * -> * |
| 67 | +% Haskell: Functor Deriving https://mail.haskell.org/pipermail/haskell-prime/2007-March/002137.html |
| 68 | +%% struct_rule(class(ClassName, class_arg(_, kind(arr(_KL, _KR))), _), Rule) :- |
| 69 | +%% member(Rule, [ |
| 70 | +%% (rule(ClassName, ty_app(TL, TR)) :- |
| 71 | +%% ( |
| 72 | +%% rule(ClassName, TL), |
| 73 | +%% apply(TL, TR, Res), |
| 74 | +%% rule(ClassName, Res) |
| 75 | +%% )), |
| 76 | +%% (rule(ClassName, ty_abs(_A, B)) :- |
| 77 | +%% ( |
| 78 | +%% rule(ClassName, B) |
| 79 | +%% )) |
| 80 | +%% ] |
| 81 | +%% ). |
| 82 | + |
| 83 | + |
| 84 | + |
| 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(*, *))), []). |
| 89 | + |
| 90 | + |
| 91 | +derive(Tys, CName, StructRules, UserRules) :- |
| 92 | + class_def(CName, CArg, CSups), |
| 93 | + findall(StructRule, |
| 94 | + ( |
| 95 | + struct_rule( |
| 96 | + class(CName, CArg, CSups), |
| 97 | + StructRule |
| 98 | + ) |
| 99 | + ), |
| 100 | + StructRules |
| 101 | + ), |
| 102 | + findall(UserRule, |
| 103 | + ( |
| 104 | + member(Ty, Tys), |
| 105 | + derive_rule( |
| 106 | + Ty, |
| 107 | + class(CName, CArg, CSups), |
| 108 | + UserRule |
| 109 | + ) |
| 110 | + ), |
| 111 | + UserRules |
| 112 | + ). |
| 113 | + |
| 114 | +solve(StructRules, UserRules, Goal) :- |
| 115 | + Goal =.. [ClassName, Ty], |
| 116 | + append(StructRules, UserRules, Rules), |
| 117 | + eval_rule(Rules, [], rule(ClassName, Ty)) -> true; |
| 118 | + ( |
| 119 | + print_message(error, rule_failed(Goal)), |
| 120 | + fail |
| 121 | + ). |
| 122 | + |
| 123 | +eval_rule(_, _, true) :- |
| 124 | + print_message(informational, rule_true). |
| 125 | + |
| 126 | +eval_rule(Rules, Trace, (RL,RR)) :- |
| 127 | + eval_rule(Rules, Trace, RL), |
| 128 | + eval_rule(Rules, Trace, RR). |
| 129 | + |
| 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; |
| 133 | + ( |
| 134 | + print_message(informational, lookup(rule(ClassName, Ty))), |
| 135 | + 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))) |
| 140 | + ); |
| 141 | + ( |
| 142 | + print_message(error, missing_rule(rule(ClassName, Ty), Trace)), |
| 143 | + fail |
| 144 | + ) |
| 145 | + ). |
| 146 | + |
| 147 | +:- multifile prolog:message//1. |
| 148 | + |
| 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]]. |
| 158 | + |
| 159 | +:- begin_tests(class_check). |
| 160 | + |
| 161 | +test("should_succeed: derive Eq Int)", []) :- |
| 162 | + derive([ty_ref(int8)], eq, S, U), |
| 163 | + solve(S, U, eq(ty_ref(int8))). |
| 164 | + |
| 165 | +test("should_succeed: derive Eq Maybe Int8)", []) :- |
| 166 | + derive([ty_ref(int8), ty_app(ty_ref(maybe), ty_ref(int8))], eq, S, U), |
| 167 | + solve(S, U, eq(ty_ref(int8))), |
| 168 | + solve(S, U, eq(ty_app(ty_ref(maybe), ty_ref(int8)))). |
| 169 | + |
| 170 | +test("should_succeed: derive Eq (Maybe a))", []) :- |
| 171 | + derive([ty_app(ty_ref(maybe), _A)], eq, S, U), |
| 172 | + solve(S, U, eq(ty_app(ty_ref(maybe), _B))). |
| 173 | + |
| 174 | +test("should_fail(derive Eq Foo)", [ fail ]) :- |
| 175 | + derive([ty_ref(foo)], eq, S, U), |
| 176 | + solve(S, U, eq(ty_ref(foo))). |
| 177 | + |
| 178 | +test("should_fail(derive Eq Foo; derive Eq Int8)", [ fail ]) :- |
| 179 | + derive([ty_ref(int8), ty_ref(foo)], eq, S, U), |
| 180 | + solve(S, U, eq(ty_ref(int8))), |
| 181 | + solve(S, U, eq(ty_ref(foo))). |
| 182 | + |
| 183 | +test("should_succeed: derive Eq Foo; derive Eq Int8)", [ ]) :- |
| 184 | + derive([ |
| 185 | + ty_ref(int8), |
| 186 | + ty_ref(foo), |
| 187 | + ty_app(ty_ref(maybe), _A) |
| 188 | + ], eq, S, U), |
| 189 | + solve(S, U, eq(ty_ref(int8))), |
| 190 | + solve(S, U, eq(ty_ref(foo))), |
| 191 | + solve(S, U, eq(ty_app(ty_ref(maybe), _B))). |
| 192 | + |
| 193 | +test("should_fail: derive Eq Recfoo", [ fail ]) :- |
| 194 | + derive([ |
| 195 | + ty_ref(recfoo) |
| 196 | + ], eq, S, U), |
| 197 | + solve(S, U, eq(ty_ref(recfoo))). |
| 198 | + |
| 199 | +test("should_succeed: derive Eq Recfoo, Eq Recbar)", [ ]) :- |
| 200 | + derive([ |
| 201 | + ty_ref(recfoo), |
| 202 | + ty_ref(recbar) |
| 203 | + ], eq, S, U), |
| 204 | + solve(S, U, eq(ty_ref(recfoo))). |
| 205 | + |
| 206 | +test("should_succeed: derive Maybe (Maybe Int8))", [ ]) :- |
| 207 | + derive([ |
| 208 | + ty_ref(int8), |
| 209 | + ty_app(ty_ref(maybe), _A) |
| 210 | + ], eq, S, U), |
| 211 | + solve(S, U, eq(ty_app(ty_ref(maybe), ty_app(ty_ref(maybe), ty_ref(int8))))). |
| 212 | + |
| 213 | +test("should_succeed: derive Maybe (Maybe a)", [ ]) :- |
| 214 | + derive([ |
| 215 | + ty_ref(int8), |
| 216 | + ty_app(ty_ref(maybe), _A) |
| 217 | + ], eq, S, U), |
| 218 | + solve(S, U, eq(ty_app(ty_ref(maybe), ty_app(ty_ref(maybe), _B)))). |
| 219 | + |
| 220 | +:- end_tests(class_check). |
0 commit comments