|
Prolog variables are metavariables, if you use them as such. ?- abstract_type(lambda(f, lambda(g, apply(f, g))), Typ).
Typ = [_G947, _G948]^ ((_G947->_G948)->_G947->_G948) .
?- abstract_type(lambda(f, lambda(g, apply(f, g))), Typ), apply_type(Typ, a, Ta), apply_type(Ta, b, Tab), apply_type(Typ, c, Tc), apply_type(Tc, d, Tcd).
Typ = [_G1179, _G1180]^ ((_G1179->_G1180)->_G1179->_G1180),
Ta = [_G1213]^ ((a->_G1213)->a->_G1213),
Tab = ((a->b)->a->b),
Tc = [_G1251]^ ((c->_G1251)->c->_G1251),
Tcd = ((c->d)->c->d) .
Note how both Tab and Tcd arise from instantiating the same schema Typ, which is unchanged.To avoid binding variables in the schema, you just make sure to only instantiate instances (copies) of the schema: abstract_type(Term, Typ) :-
type(Term, [], T), % type/3 defined by parent
term_variables(T, Vars),
Typ = Vars^T.
apply_type(Typ, Arg, Result) :-
copy_term(Typ, TypCopy),
TypCopy = Vars^T,
Vars = [Arg | RemainingVars],
( RemainingVars = []
-> Result = T
; Result = RemainingVars^T ).
|
Just to expand on my previous example, I can simply modify how bindings are looked up and generalize the associated type when it is not unknown:
Let's add two rules for tuples and addition, and change the syntax of let for clarity: Then, typing this: ... gives: You could also make use of meta attributes, ie. data stored inside variables, to control what happens during unification.