Thank you for this interesting predicates.
I also tried to make my mi a meta_predicate and use the builtin module resolver but I couldn’t make it work reliably.
Here is my own attempt at this by explicitly reifying the context module in an argument of the meta interpreter, if anyone is interested in comments and remarks:
:- module(mi, [mi/1, mi/2]).
mi(Goal) :-
mi(user, Goal).
mi(_, true) => true.
mi(Ctx, (A, B)) =>
mi(Ctx, A),
mi(Ctx, B).
mi(Ctx, Goal) =>
qualify_meta_predicate(Ctx, Goal, NewGoal),
meta_call(Ctx, NewGoal, Ctx1, Goal1),
imported_from(Ctx1, Goal1, Ctx2, Goal2),
@(clause(Goal2, Body), Ctx2),
mi(Ctx2, Body).
Basically, to correctly interpret meta predicate, one has to:
- qualify goal arguments of meta predicates with the context module at the calling site
- unwrap
call(mygoal, Arg)
to mygoal(Arg)
- resolve the implementation module of imported predicate
Did I forget anything ?
This mi pass the test case I wrote above.
The @/2
predicate is a lifesaver in this case to manipulate the context module for helper predicates like clause
, predicate_property
or strip_module
.
Here are the helper predicates used in the last clause of mi/2
:
meta_call(Ctx, Goal, NewCtx, NewGoal),
strip_module(Goal, _, Call), Call =.. [call, SubGoal | Args] =>
add_args(SubGoal, Args, NewSubGoal),
@(strip_module(NewSubGoal, NewCtx, NewGoal), Ctx).
meta_call(Ctx, Goal, NewCtx, NewGoal) =>
NewCtx = Ctx, NewGoal = Goal.
imported_from(Ctx, Goal, NewCtx, NewGoal),
@(predicate_property(Goal, imported_from(From)), Ctx) =>
NewCtx = From,
strip_module(Goal, _, NewGoal).
imported_from(Ctx, Goal, NewCtx, NewGoal) =>
NewCtx = Ctx, NewGoal = Goal.
qualify_meta_predicate(Ctx, Goal, NewGoal),
@(predicate_property(Goal, meta_predicate(Spec)), Ctx) =>
Goal =.. [Name | GoalArgs],
Spec =.. [Name | SpecArgs],
maplist(qualify_arg(Ctx), GoalArgs, SpecArgs, NewArgs),
NewGoal =.. [Name | NewArgs].
qualify_meta_predicate(_, Goal, NewGoal) =>
NewGoal = Goal.
qualify_arg(_, Mod:Goal, Spec, R), number(Spec) =>
R = Mod:Goal.
qualify_arg(Ctx, Goal, Spec, R), number(Spec) =>
R = Ctx:Goal.
qualify_arg(_, Arg, _, R) => R = Arg.
add_args(Mod:Goal, AdditionalArgs, R) =>
add_args(Goal, AdditionalArgs, NewGoal),
R = Mod:NewGoal.
add_args(Goal, AdditionalArgs, NewGoal) =>
Goal =.. [Name | Args],
append(Args, AdditionalArgs, NewArgs),
NewGoal =.. [Name | NewArgs].