Note: This is a topic for using message_hook/3 with SWI-Prolog.
If you have Trust level: Basic you can edit this Wiki topic by clicking on the edit icon in the lower right.
Note: Do not reply to this topic; questions, concerns, comments, etc. are to be handled in
Wiki Discussion: Using message_hook/3
message_hook/3 as the name implies is a hook.
For an overview of SWI-Prolog hooks see: An overview of hook predicates.
message_hook/3 is called using module user, i.e. user:message_hook(A,B,C)
instead of message_hook(A,B,C)
.
Often when using such predicates for the first time having real world example code to look at is helpful. Such examples from the SWI-Prolog source code are referenced in the following.
The hook can be a static predicate (example 1) or a dynamic predicate that is asserted. (example 2,3).
Some other hooks define dynamic/1 and multifile/1 in init.pl, message_hook/3 does not.
If message_hook/3 is defined in more than one file then use multifile/1 to suppress the warning message.
Use dynamic/1 if you want to allow dynamic modification later, e.g. retract/1 and assert/1.
A simple example of message_hook/3
This will just take the values received from the messages and print them to the current output stream.
user:message_hook(Term, Kind, Lines) :-
format('Term: ~w, Kind: ~w, Lines: ~w~n',[Term,Kind,Lines]).
A demonstration of the simple example
Directory: C:/Users/Groot
(You can change the directory as needed).
File: format_example.pl
user:message_hook(Term, Kind, Lines) :-
format('Term: ~w, Kind: ~w, Lines: ~w~n',[Term,Kind,Lines]).
Example run
Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.28-20)
...
?- working_directory(_,'C:/Users/Groot').
true.
?- [format_example].
Term: load_file(done(0,file(format_example,c:/users/Groot/format_example.pl),compiled,user,0.0,1)), Kind: silent, Lines: [~|~t~*+ - [0],~w-[format_example], ~w-[compiled], ~2f sec, ~D clauses-[0.0,1]]
Term: query(yes([],true,[]-[])), Kind: query, Lines: [ansi(truth(true),true.,[]),~N-[]]
An example that uses print_message/2.
This shows both the input values output using format/2 and then uses print_message/2 to print a message. Notice that messages with Kind
is silent
print_message/2 does not print a message.
Directory: C:/Users/Groot
(You can change the directory as needed).
File: print_message_example.pl
user:message_hook(Term, Kind, Lines) :-
format('Term: ~w~nKind: ~w~nLines: ~w~n~n',[Term,Kind,Lines]),
print_message(Kind, Term),
nl,nl.
:- append([1,2],[3,4],R).
Example run (Click triangle to expand)
Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.28-20)
...
?- working_directory(_,'C:/Users/Groot').
true.
?- [print_message_example].
Term: singletons((:-append([1,2],[3,4],_20202)),[R])
Kind: warning
Lines: [Singleton variables: ~w-[[R]]]
Recursive warning message: singletons((:-append([1,2],[3,4],_20202)),['R'])
Term: autoload(user:append/3,c:/program files/swipl/library/lists)
Kind: silent
Lines: [autoloading ~p from ~w-[user:append/3,c:/program files/swipl/library/lists]]
Term: load_file(start(1,file(c:/program files/swipl/library/lists,c:/program files/swipl/library/lists.pl)))
Kind: silent
Lines: [~|~t~*+Loading -[1],~w-[c:/program files/swipl/library/lists], ...]
Term: load_file(done(1,file(c:/program files/swipl/library/lists,c:/program files/swipl/library/lists.pl),compiled,lists,0.0,111))
Kind: silent
Lines: [~|~t~*+ - [1],~w-[c:/program files/swipl/library/lists], ~w-[compiled], into ~w-[lists], ~2f sec, ~D clauses-[0.0,111]]
Term: load_file(done(0,file(print_message_example,c:/users/groot/print_message_example.pl),compiled,user,0.0,2))
Kind: silent
Lines: [~|~t~*+ - [0],~w-[print_message_example], ~w-[compiled], ~2f sec, ~D clauses-[0.0,2]]
Term: query(yes([],true,[]-[]))
Kind: query
Lines: [ansi(truth(true),true.,[]),~N-[]]
Recursive query message: query(yes([],true,[]-[]))
An example that uses message_to_string/2.
Directory: C:/Users/Groot
(You can change the directory as needed).
File: message_to_string_example.pl
user:message_hook(Term, Kind, Lines) :-
format('Term: ~w~nKind: ~w~nLines: ~w~n~n',[Term,Kind,Lines]),
message_to_string(Term,String),
format('~s~n',[String]),
nl,nl.
:- append([1,2],[3,4],R).
Example run (Click triangle to expand)
Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.28-20)
...
?- working_directory(_,'C:/Users/Groot').
true.
?- [message_to_string_example].
Term: singletons((:-append([1,2],[3,4],_20202)),[R])
Kind: warning
Lines: [Singleton variables: ~w-[[R]]]
Singleton variables: [R]
Term: autoload(user:append/3,c:/program files/swipl/library/lists)
Kind: silent
Lines: [autoloading ~p from ~w-[user:append/3,c:/program files/swipl/library/lists]]
autoloading user:append/3 from c:/program files/swipl/library/lists
Term: load_file(start(1,file(c:/program files/swipl/library/lists,c:/program files/swipl/library/lists.pl)))
Kind: silent
Lines: [~|~t~*+Loading -[1],~w-[c:/program files/swipl/library/lists], ...]
Loading c:/program files/swipl/library/lists ...
Term: load_file(done(1,file(c:/program files/swipl/library/lists,c:/program files/swipl/library/lists.pl),compiled,lists,0.015625,111))
Kind: silent
Lines: [~|~t~*+ - [1],~w-[c:/program files/swipl/library/lists], ~w-[compiled], into ~w-[lists], ~2f sec, ~D clauses-[0.015625,111]]
c:/program files/swipl/library/lists compiled into lists 0.02 sec, 111 clauses
Term: load_file(done(0,file(message_to_string_example,c:/users/groot/message_to_string_example.pl),compiled,user,0.015625,2))
Kind: silent
Lines: [~|~t~*+ - [0],~w-[message_to_string_example], ~w-[compiled], ~2f sec, ~D clauses-[0.015625,2]]
message_to_string_example compiled 0.02 sec, 2 clauses
Term: query(yes([],true,[]-[]))
Kind: query
Lines: [ansi(truth(true),true.,[]),~N-[]]
true.
An example that uses print_message_lines/3.
Directory: C:/Users/Groot
(You can change the directory as needed).
File: print_message_lines_example.pl
user:message_hook(Term, Kind, Lines) :-
format('Term: ~w~nKind: ~w~nLines: ~w~n~n',[Term,Kind,Lines]),
print_message_lines(current_output,kind(Kind),Lines),
nl.
:- append([1,2],[3,4],R).
Example run (Click triangle to expand)
Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.28-20)
...
?- working_directory(_,'C:/Users/Groot').
true.
?- [print_message_lines_example].
Term: singletons((:-append([1,2],[3,4],_20202)),[R])
Kind: warning
Lines: [Singleton variables: ~w-[[R]]]
Warning: Singleton variables: [R]
Term: autoload(user:append/3,c:/program files/swipl/library/lists)
Kind: silent
Lines: [autoloading ~p from ~w-[user:append/3,c:/program files/swipl/library/lists]]
autoloading user:append/3 from c:/program files/swipl/library/lists
Term: load_file(start(1,file(c:/program files/swipl/library/lists,c:/program files/swipl/library/lists.pl)))
Kind: silent
Lines: [~|~t~*+Loading -[1],~w-[c:/program files/swipl/library/lists], ...]
Loading c:/program files/swipl/library/lists ...
Term: load_file(done(1,file(c:/program files/swipl/library/lists,c:/program files/swipl/library/lists.pl),compiled,lists,0.015625,111))
Kind: silent
Lines: [~|~t~*+ - [1],~w-[c:/program files/swipl/library/lists], ~w-[compiled], into ~w-[lists], ~2f sec, ~D clauses-[0.015625,111]]
c:/program files/swipl/library/lists compiled into lists 0.02 sec, 111 clauses
Term: load_file(done(0,file(print_message_lines_example,c:/users/groot/print_message_lines_example.pl),compiled,user,0.015625,2))
Kind: silent
Lines: [~|~t~*+ - [0],~w-[print_message_lines_example], ~w-[compiled], ~2f sec, ~D clauses-[0.015625,2]]
print_message_lines_example compiled 0.02 sec, 2 clauses
Term: query(yes([],true,[]-[]))
Kind: query
Lines: [ansi(truth(true),true.,[]),~N-[]]
true.
An example that prints informational messages using unit test.
Directory: C:/Users/Groot
(You can change the directory as needed).
File: print_message_lines_example.pl
user:message_hook(Term, Kind, Lines) :-
format('Term: ~w~nKind: ~w~nLines: ~w~n~n',[Term,Kind,Lines]),
print_message_lines(current_output,kind(Kind),Lines),
nl.
:- begin_tests(message_hook).
:- use_module(library(lists)).
test(reverse) :-
reverse([a,b], [b,a]).
:- end_tests(message_hook).
Note: In the example run below, the informational messages are not generated when the code is loaded, the informational messages are generated for run_tests/0.
Example run (Click triangle to expand)
Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.28-20)
...
?- [informational_message_example].
Term: autoload(user:begin_tests/1,c:/program files/swipl/library/plunit)
Kind: silent
Lines: [autoloading ~p from ~w-[user:begin_tests/1,c:/program files/swipl/library/plunit]]
autoloading user:begin_tests/1 from c:/program files/swipl/library/plunit
Term: load_file(start(1,file(c:/program files/swipl/library/plunit,c:/program files/swipl/library/plunit.pl)))
Kind: silent
Lines: [~|~t~*+Loading -[1],~w-[c:/program files/swipl/library/plunit], ...]
Loading c:/program files/swipl/library/plunit ...
Term: load_file(done(1,file(c:/program files/swipl/library/plunit,c:/program files/swipl/library/plunit.pl),compiled,plunit,0.015625000000000028,280))
Kind: silent
Lines: [~|~t~*+ - [1],~w-[c:/program files/swipl/library/plunit], ~w-[compiled], into ~w-[plunit], ~2f sec, ~D clauses-[0.015625000000000028,280]]
c:/program files/swipl/library/plunit compiled into plunit 0.02 sec, 280 clauses
Term: autoload(plunit:must_be/2,c:/program files/swipl/library/error.pl)
Kind: silent
Lines: [autoloading ~p from ~w-[plunit:must_be/2,c:/program files/swipl/library/error.pl]]
autoloading plunit:must_be/2 from c:/program files/swipl/library/error.pl
Term: load_file(start(2,file(c:/program files/swipl/library/error.pl,c:/program files/swipl/library/error.pl)))
Kind: silent
Lines: [~|~t~*+Loading -[2],~w-[c:/program files/swipl/library/error.pl], ...]
Loading c:/program files/swipl/library/error.pl ...
Term: load_file(done(2,file(c:/program files/swipl/library/error.pl,c:/program files/swipl/library/error.pl),compiled,error,0.015625,90))
Kind: silent
Lines: [~|~t~*+ - [2],~w-[c:/program files/swipl/library/error.pl], ~w-[compiled], into ~w-[error], ~2f sec, ~D clauses-[0.015625,90]]
c:/program files/swipl/library/error.pl compiled into error 0.02 sec, 90 clauses
Term: autoload(plunit:option/3,c:/program files/swipl/library/option.pl)
Kind: silent
Lines: [autoloading ~p from ~w-[plunit:option/3,c:/program files/swipl/library/option.pl]]
autoloading plunit:option/3 from c:/program files/swipl/library/option.pl
Term: load_file(start(2,file(c:/program files/swipl/library/option.pl,c:/program files/swipl/library/option.pl)))
Kind: silent
Lines: [~|~t~*+Loading -[2],~w-[c:/program files/swipl/library/option.pl], ...]
Loading c:/program files/swipl/library/option.pl ...
Term: load_file(done(2,file(c:/program files/swipl/library/option.pl,c:/program files/swipl/library/option.pl),compiled,swi_option,0.0,42))
Kind: silent
Lines: [~|~t~*+ - [2],~w-[c:/program files/swipl/library/option.pl], ~w-[compiled], into ~w-[swi_option], ~2f sec, ~D clauses-[0.0,42]]
c:/program files/swipl/library/option.pl compiled into swi_option 0.00 sec, 42 clauses
Term: load_file(start(1,file(library(lists),c:/program files/swipl/library/lists.pl)))
Kind: silent
Lines: [~|~t~*+Loading -[1],~p-[library(lists)], ...]
Loading library(lists) ...
Term: load_file(done(1,file(library(lists),c:/program files/swipl/library/lists.pl),compiled,lists,0.0,111))
Kind: silent
Lines: [~|~t~*+ - [1],~p-[library(lists)], ~w-[compiled], into ~w-[lists], ~2f sec, ~D clauses-[0.0,111]]
library(lists) compiled into lists 0.00 sec, 111 clauses
Term: autoload(plunit:ord_intersection/3,c:/program files/swipl/library/ordsets.pl)
Kind: silent
Lines: [autoloading ~p from ~w-[plunit:ord_intersection/3,c:/program files/swipl/library/ordsets.pl]]
autoloading plunit:ord_intersection/3 from c:/program files/swipl/library/ordsets.pl
Term: load_file(start(2,file(c:/program files/swipl/library/ordsets.pl,c:/program files/swipl/library/ordsets.pl)))
Kind: silent
Lines: [~|~t~*+Loading -[2],~w-[c:/program files/swipl/library/ordsets.pl], ...]
Loading c:/program files/swipl/library/ordsets.pl ...
Term: load_file(done(2,file(c:/program files/swipl/library/ordsets.pl,c:/program files/swipl/library/ordsets.pl),compiled,ordsets,0.0,88))
Kind: silent
Lines: [~|~t~*+ - [2],~w-[c:/program files/swipl/library/ordsets.pl], ~w-[compiled], into ~w-[ordsets], ~2f sec, ~D clauses-[0.0,88]]
c:/program files/swipl/library/ordsets.pl compiled into ordsets 0.00 sec, 88 clauses
Term: autoload(plunit:maplist/3,c:/program files/swipl/library/apply.pl)
Kind: silent
Lines: [autoloading ~p from ~w-[plunit:maplist/3,c:/program files/swipl/library/apply.pl]]
autoloading plunit:maplist/3 from c:/program files/swipl/library/apply.pl
Term: load_file(start(2,file(c:/program files/swipl/library/apply.pl,c:/program files/swipl/library/apply.pl)))
Kind: silent
Lines: [~|~t~*+Loading -[2],~w-[c:/program files/swipl/library/apply.pl], ...]
Loading c:/program files/swipl/library/apply.pl ...
Term: load_file(done(2,file(c:/program files/swipl/library/apply.pl,c:/program files/swipl/library/apply.pl),compiled,apply,0.0,75))
Kind: silent
Lines: [~|~t~*+ - [2],~w-[c:/program files/swipl/library/apply.pl], ~w-[compiled], into ~w-[apply], ~2f sec, ~D clauses-[0.0,75]]
c:/program files/swipl/library/apply.pl compiled into apply 0.00 sec, 75 clauses
Term: autoload(plunit:include/3,c:/program files/swipl/library/apply.pl)
Kind: silent
Lines: [autoloading ~p from ~w-[plunit:include/3,c:/program files/swipl/library/apply.pl]]
autoloading plunit:include/3 from c:/program files/swipl/library/apply.pl
Term: autoload(plunit_message_hook:end_tests/1,c:/program files/swipl/library/plunit)
Kind: silent
Lines: [autoloading ~p from ~w-[plunit_message_hook:end_tests/1,c:/program files/swipl/library/plunit]]
autoloading plunit_message_hook:end_tests/1 from c:/program files/swipl/library/plunit
Term: load_file(done(0,file(informational_message_example,c:/users/groot/informational_message_example.pl),compiled,user,0.03125000000000003,9))
Kind: silent
Lines: [~|~t~*+ - [0],~w-[informational_message_example], ~w-[compiled], ~2f sec, ~D clauses-[0.03125000000000003,9]]
informational_message_example compiled 0.03 sec, 9 clauses
Term: query(yes([],true,[]-[]))
Kind: query
Lines: [ansi(truth(true),true.,[]),~N-[]]
true.
?- run_tests.
Term: autoload(user:run_tests/0,c:/program files/swipl/library/plunit)
Kind: silent
Lines: [autoloading ~p from ~w-[user:run_tests/0,c:/program files/swipl/library/plunit]]
autoloading user:run_tests/0 from c:/program files/swipl/library/plunit
Term: toplevel_goal(user:run_tests,[])
Kind: silent
Lines: [Unknown message: ~p-[toplevel_goal(user:run_tests,[])]]
Unknown message: toplevel_goal(user:run_tests,[])
Term: autoload(plunit:option/2,c:/program files/swipl/library/option.pl)
Kind: silent
Lines: [autoloading ~p from ~w-[plunit:option/2,c:/program files/swipl/library/option.pl]]
autoloading plunit:option/2 from c:/program files/swipl/library/option.pl
Term: autoload(plunit:debug/3,c:/program files/swipl/library/debug)
Kind: silent
Lines: [autoloading ~p from ~w-[plunit:debug/3,c:/program files/swipl/library/debug]]
autoloading plunit:debug/3 from c:/program files/swipl/library/debug
Term: load_file(start(1,file(c:/program files/swipl/library/debug,c:/program files/swipl/library/debug.pl)))
Kind: silent
Lines: [~|~t~*+Loading -[1],~w-[c:/program files/swipl/library/debug], ...]
Loading c:/program files/swipl/library/debug ...
Term: load_file(done(1,file(c:/program files/swipl/library/debug,c:/program files/swipl/library/debug.pl),compiled,prolog_debug,0.015625,48))
Kind: silent
Lines: [~|~t~*+ - [1],~w-[c:/program files/swipl/library/debug], ~w-[compiled], into ~w-[prolog_debug], ~2f sec, ~D clauses-[0.015625,48]]
c:/program files/swipl/library/debug compiled into prolog_debug 0.02 sec, 48 clauses
Term: plunit(begin(message_hook))
Kind: informational
Lines: [PL-Unit: ~w -[message_hook],flush]
% PL-Unit: message_hook
Term: plunit(begin(message_hook:reverse,c:/users/groot/informational_message_example.pl:26,rational_trees))
Kind: silent
Lines: [Unknown message: ~p-[plunit(begin(message_hook:reverse,c:/users/groot/informational_message_example.pl:26,rational_trees))]]
Unknown message: plunit(begin(message_hook:reverse,'c:/users/groot/informational_message_example.pl':26,rational_trees))
Term: plunit(end(message_hook:reverse,c:/users/groot/informational_message_example.pl:26,rational_trees))
Kind: silent
Lines: [Unknown message: ~p-[plunit(end(message_hook:reverse,c:/users/groot/informational_message_example.pl:26,rational_trees))]]
Unknown message: plunit(end(message_hook:reverse,'c:/users/groot/informational_message_example.pl':26,rational_trees))
Term: plunit(progress(message_hook,reverse,passed))
Kind: information
Lines: [at_same_line,(.)-[],flush]
.
Term: plunit(end(message_hook))
Kind: informational
Lines: [at_same_line, done]
done
Term: plunit(summary(plunit{blocked:0,failed:0,failed_assertions:0,passed:1,sto:0}))
Kind: silent
Lines: [Unknown message: ~p-[plunit(summary(plunit{blocked:0,failed:0,failed_assertions:0,passed:1,sto:0}))]]
Unknown message: plunit(summary(plunit{blocked:0,failed:0,failed_assertions:0,passed:1,sto:0}))
Term: plunit(fixme(0,0,0))
Kind: informational
Lines: []
%
Term: plunit(all_passed(1))
Kind: informational
Lines: [test passed]
% test passed
Term: query(yes([],true,[]-[]))
Kind: query
Lines: [ansi(truth(true),true.,[]),~N-[]]
true.
Notes:
One would think that if the hook is asserted then assert/1 would be used to assert message_hook/3, typically asserta/2 is used to capture the reference to the asserted clause.
The reason that temporary installed hooks use
setup_call_cleanup(
asserta(HookTerm, Ref),
ProtectedCode,
erase(Ref))
Is because this makes it trivial to remove exactly the added clause. If we use retract/1, it may retract another clause that happens to unify with the one you just added. It may also leave a choice point. Using erase/1 guarantee we have the right clause without any search and without unwanted choice points. (ref)
SWI-Prolog is written in C and low level Prolog constructs such as a clause are actually C structures (struct clause).
A side benefit of having the reference is that you can use it with certain predicates like clause_property/2.
For more clarification on the differences between static and dynamic predicates with SWI-Prolog see this other reply by Jan W.