For review: Improve debugging printout under Prolog

Peter Schachte pets at cs.mu.oz.au
Tue Oct 28 18:15:20 AEDT 1997


Improve debugging printout under Prolog

Estimated hours taken: 15


library/portray.nl:
	You can now control the depth limit, and separately the limit
	on the lengths of lists and the number of arguments shown for
	high-arity terms.  You can also "turn off" certain arguments
	of certain terms, based on the functor of the term, or even
	turn off the whole term.  Finally, you can define your own
	print methods for terms if you like, so lists of character
	codes can be printed as double-quoted strings, and maps can be
	printed out as associations.  Both of these are provided, and
	serve as examples.

doc/user_guide.texi:
	Documented all the above.


Index: library/portray.nl
===================================================================
RCS file: /home/staff/zs/imp/mercury/library/portray.nl,v
retrieving revision 1.16
diff -u -r1.16 portray.nl
--- portray.nl	1997/07/27 15:07:02	1.16
+++ portray.nl	1997/10/24 08:16:32
@@ -5,70 +5,444 @@
 %-----------------------------------------------------------------------------%
 %
 % File: portray.nl
-% Main author: fjh.
+% Main authors: fjh, pets.
 %
-% This file contains a definition for portray/1 that only
-% displays the top-level functors of big terms.
-% This is useful for debugging, since otherwise the NU-Prolog
-% debugger prints out screenfuls of crap as you step through
-% a program.  It also contains a definition for spyHook/2 that displays
-% the term in full, so that you can get the full details by
-% typing "|" in the debugger.
-% Note that you need to compile it to a .no file -
-% loading it as a .np file doesn't work.
+% This file defines portray/1 (and thereby defines print/1) to print terms in
+% a controlled way, so you don't wind up having to wade through several
+% screenfuls of stuff to find the bit you're interested in.  Note that query
+% results and debugging state are printed out using print/1.  You can control
+% the depth limit, and separately the limit on the lengths of lists and the
+% number of arguments shown for high-arity terms.  You can also "turn off"
+% certain arguments of certain terms, based on the functor of the term, or
+% even turn off the whole term.  Finally, you can define your own print
+% methods for terms if you like, so lists of character codes can be printed as
+% double-quoted strings, and maps can be printed out as associations.  Both of
+% these are provided as examples.
+% 
+% This file also contains a definition for spyHook/2 that displays the term in
+% full, so that you can get the full details by typing "|" in the debugger.
+% Note that you need to compile it to a .no file - loading it as a .np file
+% doesn't work.
 %
 %-----------------------------------------------------------------------------%
 
-historyLength(500).
+:- dynamic print_depth/1.
+:- dynamic print_length/1.
+:- dynamic hidden/3.
+
 
+%  Make the standard Prolog print/1 call my print_term/1.
 portray(Term) :-
 	!,
-	myportray(Term).
+	print_term(Term).
+
+
+%  print_depth(-Depth)
+%  The current print depth limit is Depth.  If the depth limit is less than 0,
+%  the print depth is unlimited.
+
+print_depth(2).
+
+
+%  set_print_depth(+Depth)
+%  Set the print depth limit to Depth.  If the depth limit is less than 0,
+%  the print depth is unlimited.
+
+set_print_depth(Depth) :-
+	retractall(print_depth(_)),
+	assert(print_depth(Depth)).
+
+
+%  print_length(-Length)
+%  The current print length limit is Length.  The length limit controls the
+%  display of long lists and terms with many arguments.  If the length limit
+%  is less than 0, the print length is unlimited.
+
+print_length(10).
+
+
+%  set_print_length(+Length)
+%  Set the print length limit to Length.  If the length limit is less than 0,
+%  the print length is unlimited.
+
+set_print_length(New) :-
+	retractall(print_length(_)),
+	assert(print_length(New)).
+
 
-myportray(Term) :-
-	( compound(Term), list_of_char_codes(Term) ->
-		format("""~s""", [Term])
-	; compound(Term) ->
-		Term =.. [F|Args],
-		write(F),
-		write('('),
-		portray_args(Args),
-		write(')')
+%  hide(+Spec)
+%  hide(+Spec, +Args)
+%  This predicate allows you to elide certain arguments of certain terms, or
+%  whole terms, based on the functor.  Spec specifies a functor; it must be
+%  either a Name/Arity, or just a functor name, in which case all arities are
+%  specified.  Args specifies which arguments of such terms should be hidden.
+%  Hidden terms are printed as just a hash (#) character.  Args, if given,
+%  must be a list of integers specifying argument numbers to hide;
+%  alternatively, it may be a single argument number to specify a single
+%  argument to hide, or it mmay be the atom 'term', in which case only the
+%  name and arity of the term are written within angle brackets, (e.g.,
+%  <foo/3>).  If Args is not specified, it defaults to 'term'.
+
+hide(Spec, Arg) :-
+	Predspec = "hide/2",
+	term_spec(Spec, Name, Arity, Predspec),
+	argument_spec(Arg, Args, Predspec),
+	hide_term_args(Name, Arity, Args).
+
+hide(Spec) :-
+	term_spec(Spec, Name, Arity, "hide/1"),
+	hide_term_args(Name, Arity, term).
+
+	
+hide_term_args(Name, Arity, Args) :-
+	(   retract(hidden(Name, Arity, OldHidden)) ->
+		true
 	;
-		write(Term)
+		OldHidden = []
+	),
+	(   Args == term ->
+		NewHidden = term
+	;   OldHidden == term ->
+		NewHidden = term
+	;
+		append(Args, OldHidden, NewHidden1),
+		sort(NewHidden1, NewHidden)
+	),
+	(   var(Arity) -> 
+		assertz(hidden(Name, Arity, NewHidden))
+	;
+		asserta(hidden(Name, Arity, NewHidden))
+	).
+
+
+%  show(+Spec, +Args)
+%  show(+Spec)
+%  show
+%  Undo the effect of hiding argumments of some terms.  If Spec is given, stop
+%  hiding the indicated arguments of the specified terms.  Spec is exactly as
+%  for hide/2.  Args specifies which arguments to stop hiding; it must be a
+%  list of argument numbers, or a single argument number, or the atom 'term',
+%  indicating that no arguments should be hidden.  Args defaults to 'term'.
+%  If Spec is not given, no arguments of any term will be hidden.
+%
+%  Note that these predicates will not affect the depth limit of printing.
+%  Even if you ask to show a term in full, if it appears at the depth limit of
+%  the term being printed, its arguments will not be shown.  If it appears
+%  below the depth limit, you won't see it at all.
+
+show(Spec, Args0) :-
+	Predspec = "show/2",
+	term_spec(Spec, Name, Arity, Predspec),
+	argument_spec(Args0, Args, Predspec),
+	show_term_args(Name, Arity, Args).
+
+show(Spec) :-
+	term_spec(Spec, Name, Arity, "show/1"),
+	show_term_args(Name, Arity, term).
+
+show :-
+	retractall(hidden(_,_,_)).
+
+
+show_term_args(Name, Arity, Args) :-
+	(   retract(hidden(Name, Arity, OldHidden0)),
+	    (   Args == term ->
+		    true
+	    ;   sort(Args, Args1),
+		(   OldHidden0 = term ->
+			iota(255, OldHidden)
+		;   OldHidden = OldHidden0
+		),
+		sorted_list_difference(OldHidden, Args1, NewHidden),
+		assertz(hidden(Name, Arity, NewHidden))
+	    ),
+	    fail
+	;   true
+	).
+
+
+%  hidden(-Spec, -Args)
+%  Spec is a Name/Arity term, and Args is a list of the argument numbers of
+%  terms with that Name and Arity which are hidden, or the atom 'term' if the
+%  entire term is hidden.  Arity may be the atom 'all', indicating that the
+%  specified arguments are hidden for all terms with that Name and any arity.
+
+hidden(Spec, Args) :-
+	Spec = Name/Arity,
+	hidden(Name, Arity, Args),
+	(   var(Arity) ->
+		Arity = (all)
+	;   true
 	).
 
-portray_args([]).
-portray_args([X|Xs]) :-
-	portray2(X),
-	portray_args_2(Xs).
-
-portray_args_2([]).
-portray_args_2([X|Xs]) :-
-	write(', '),
-	portray2(X),
-	portray_args_2(Xs).
-
-portray2(Term) :-
-	( compound(Term), list_of_char_codes(Term) ->
-		format("""~s""", [Term])
-	; compound(Term) ->
-		functor(Term,F,N),
-		format("<~a/~d>", [F,N])
+
+term_spec(Spec, Name, Arity, Predspec) :-
+	(   nonvar(Spec),
+	    Spec = Name/Arity0,
+	    atom(Name) ->
+		(   integer(Arity0) ->
+			Arity = Arity0
+		;   Arity0 = (all) ->
+			true			% leave Arity unbound
+		)
+	;   atom(Spec) ->
+		Name = Spec			% leave Arity unbound
+	;
+		pred_error(Predspec, "invalid predicate specifier")
+	).
+
+
+argument_spec(Args0, Args, Predspec) :-
+	(   integer(Args0) ->
+		Args = [Args0]
+	;   Args0 == term ->
+		Args = Args0
+	;   Args0 = [_|_] ->
+		(   member(Oops, Args0),
+		    \+ integer(Oops) ->
+			pred_error(
+			error(Predspec,
+			      "non-integer in argument specification list"))
+		;
+			Args = Args0
+		)
 	;
+		pred_error(Predspec,
+			   "arg 2 must be an integer or list of integers")
+	).
+
+
+pred_error(Predspec, Msg) :-
+	string__append(": ", Msg, Back1),
+	string__append(Back1, Msg, Back2),
+	string__append("in call to ", Back2, Errmsg),
+	error(Errmsg).
+
+
+sorted_list_difference([], _, []).
+sorted_list_difference([A|As], Bs, Cs) :-
+	sorted_list_difference_1(Bs, A, As, Cs).
+
+sorted_list_difference_1([], A, As, [A|As]).
+sorted_list_difference_1([B|Bs], A, As, Cs) :-
+	compare(A, B, Comparison),
+	sorted_list_difference_2(Comparison, A, As, B, Bs, Cs).
+
+sorted_list_difference_2(<, A, As, B, Bs, [A|Cs]) :-
+	(   As == [] ->
+		Cs = []
+	;   As = [A1|As1],
+	    compare(A1, B, Comparison),
+	    sorted_list_difference_2(Comparison, A1, As1, B, Bs, Cs)
+	).
+sorted_list_difference_2(=, _, As, _, Bs, Cs) :-
+	sorted_list_difference_2(As, Bs, Cs).
+sorted_list_difference_2(>, A, As, _, Bs, Cs) :-
+	sorted_list_difference_1(Bs, A, As, Cs).
+
+
+iota(Nlow, Nhigh, L) :-
+	(   Nlow =< Nhigh ->
+		L = [Nlow|L1],
+		Nlow1 is Nlow + 1,
+		iota(Nlow1, Nhigh, L1)
+	;   L = []
+	).
+
+
+%  print_term(+Term)
+%  print_term(+Term, +DepthLimit, +Precedence)
+%  Print out Term to the current output stream, respecting the current depth
+%  and length limits, and the current set of hidden functors, and using the
+%  user-supplied portray_term/3 hook predicate when it succeeds.  If
+%  DepthLimit is supplied and is non-negative, then it specifies the maximum
+%  number of levels of Term that should be printed.  Precedence, if supplied,
+%  should be the precedence of the context in which the term will be printed.
+%  DepthLimit defaults to the currently set depth limit; Precedence defaults
+%  to 999.
+
+print_term(Term) :-
+	print_depth(Limit),
+	print_term(Term, Limit, 999).
+
+
+print_term(Term, Depth, Precedence) :-
+	(   var(Term) ->
 		write(Term)
+	;
+		Depth1 is Depth - 1,
+		(   portray_term(Term, Depth1, Precedence) ->
+			true
+		;   Depth1 =:= -1 ->
+			functor(Term, Name, Arity),
+			(   Arity =:= 0 ->
+				writeq(Name)
+			;   
+				format("<~q/~d>", [Name,Arity])
+			)
+		;   
+			standard_print_term(Term, Depth1, Precedence)
+		)
+	).
+
+
+standard_print_term(Term, Depth, Precedence) :-
+	functor(Term, Name, Arity),
+	(   hidden(Name, Arity, Hidden) ->
+		true
+	;
+		Hidden = []
+	),
+	(   Hidden == term,
+	    print_depth(Depth1),
+	    Depth =\= Depth1 - 1 ->
+		format("<~q/~d>", [Name,Arity])
+	;
+		(   Hidden == term ->
+			% Special case:  if the top level term we're
+			% printing is hidden, we print it anyway, but hide all
+			% of its arguments.  This is because it seems
+			% undesirable to hide the entire term that was to be
+			% printed.
+			Depth2 = 0
+		;
+			Depth2 = Depth
+		),
+		(   Name == '.', Arity =:= 2, Hidden == [] ->
+			print_length(Length), 
+			print_list(Term, Depth2, Length, 999,
+				   [], '[', ']', ',')
+		;   Name == '{}', Arity =:= 1 ->
+			write('{'),
+			arg(1, Term, Arg),
+			maybe_print_term(Arg, Depth2, 999, 1, Hidden, _),
+			write('}')
+		;   Arity =:= 1,
+		    prefix_op(Name, NeededPrecedence, ArgPrecedence) ->
+			open_paren_if_needed(Precedence, NeededPrecedence),
+			writeq(Name),
+			write(' '),
+			arg(1,Term, Arg),
+			maybe_print_term(Arg, Depth2, ArgPrecedence, 1,
+					 Hidden, _),
+			close_paren_if_needed(Precedence, NeededPrecedence)
+		;   Arity =:= 1,
+		    postfix_op(Name, NeededPrecedence, ArgPrecedence) ->
+			open_paren_if_needed(Precedence, NeededPrecedence),
+			arg(1,Term, Arg),
+			maybe_print_term(Arg, Depth2, ArgPrecedence, 1,
+					 Hidden, _),
+			write(' '),
+			writeq(Name),
+			close_paren_if_needed(Precedence, NeededPrecedence)
+		;   Arity =:= 2,
+		    infix_op(Name, NeededPrecedence, Arg1Precedence,
+			     Arg2Precedence) ->
+			open_paren_if_needed(Precedence, NeededPrecedence),
+			arg(1,Term, Arg1),
+			maybe_print_term(Arg1, Depth2, Arg1Precedence, 1,
+					 Hidden, H2),
+			write(' '),
+			(   Name == (',') ->
+				write(Name)
+			;
+				writeq(Name)
+			),
+			write(' '),
+			arg(2,Term, Arg2),
+			maybe_print_term(Arg2, Depth2, Arg2Precedence, 2,
+					 H2, _),
+			close_paren_if_needed(Precedence, NeededPrecedence)
+		;
+			Term =.. [Name|Args],
+			writeq(Name),
+			print_length(Length),
+			print_list(Args, Depth, Length, Precedence, Hidden,
+				   '', '(', ')', ',')
+		)
+	).
+
+prefix_op(Op, NeededPrecedence, ArgPrecedence) :-
+	(   current_op(NeededPrecedence, fx, Op) ->
+		ArgPrecedence is NeededPrecedence - 1
+	;   current_op(NeededPrecedence, fy, Op) ->
+		ArgPrecedence = NeededPrecedence
+	).
+
+
+postfix_op(Op, NeededPrecedence, ArgPrecedence) :-
+	(   current_op(NeededPrecedence, xf, Op) ->
+		ArgPrecedence is NeededPrecedence - 1
+	;   current_op(NeededPrecedence, yf, Op) ->
+		ArgPrecedence = NeededPrecedence
 	).
 
-list_of_char_codes([]).
-list_of_char_codes([X|Xs]) :-
-	integer(X),
-	( X = 0'\t -> true
-	; X = 0'\n -> true
-	; X = 0'\r -> true
-	; X >= 32
+
+infix_op(Op, NeededPrecedence, Arg1Precedence, Arg2Precedence) :-
+	(   current_op(NeededPrecedence, xfx, Op) ->
+		Arg1Precedence is NeededPrecedence - 1,
+		Arg2Precedence = Arg1Precedence
+	;   current_op(NeededPrecedence, xfy, Op) ->
+		Arg1Precedence is NeededPrecedence - 1,
+		Arg2Precedence = NeededPrecedence
+	;   current_op(NeededPrecedence, yfx, Op) ->
+		Arg1Precedence = NeededPrecedence,
+		Arg2Precedence is NeededPrecedence - 1
+	).
+
+
+print_list([], _, _, _, _, Empty, _, _, _) :-
+	write(Empty).
+print_list([T|Ts], Depth, Limit, Precedence, Hidden, _, Start, End, Sep) :-
+	write(Start),
+	(   Limit =\= 0 ->
+		maybe_print_term(T, Depth, Precedence, 1, Hidden, Hidden1),
+		Limit1 is Limit + 1,		% first arg NOT to show
+		print_list_tail(Ts, 2, Limit1, Hidden1, Depth, Precedence, Sep)
+	;   write('...')
 	),
-	X < 256,
-	list_of_char_codes(Xs).
+	write(End).
+	
+print_list_tail(Term, _, _, _, _, _, _) :-
+	var(Term),
+	!,
+	write(' | '),
+	write(Term).
+print_list_tail([], _, _, _, _, _, _).
+print_list_tail([T|Ts], Num, Limit, Hidden, Depth, Precedence, Sep) :-
+	write(Sep),
+	(   Num =\= Limit ->
+		maybe_print_term(T, Depth, Precedence, Num, Hidden, Hidden1),
+		Num1 is Num + 1,
+		print_list_tail(Ts, Num1, Limit, Hidden1, Depth, Precedence,
+				Sep)
+	;   write('...')
+	).
+
+
+maybe_print_term(T, Depth, Precedence, Num, Hidden0, Hidden) :-
+	(   Hidden0 = [Num|Hidden] ->
+		write('#')
+	;   Hidden = Hidden0,
+	    print_term(T, Depth, Precedence)
+	).
+
+
+open_paren_if_needed(Precedence, NeededPrecedence) :-
+	(   Precedence >= NeededPrecedence ->
+		true
+	;   write('(')
+	).
+
+
+close_paren_if_needed(Precedence, NeededPrecedence) :-
+	(   Precedence >= NeededPrecedence ->
+		true
+	;   write(')')
+	).
+
+
+historyLength(500).
 
 spyHook(_, Term) :-
 	!,
@@ -124,5 +498,97 @@
 	;
 		true
 	).
+
+
+%  portray_term(+Term, +DepthLimit, +Precedence)
+%  User-supplied hook predicate.  Like the standard Prolog portray/1 hook
+%  predicate, this code should either fail without printing anything, or
+%  succeed after printing out Term as the user would like to see it printed.
+%  DepthLimit, if >= -1, is the number of levels of the subterms of Term that
+%  should be printed without being elided.  If it is -1, then Term should
+%  probably not be printed; only a marker indicating what sort of term it is.
+%  The standard print_term code will print the name and arity of the functor
+%  enclosed in angle brackets (e.g., <foo/3>) if portray_term/3 fails at this
+%  depth level.  Be careful, though: if DepthLimit is negative, then there is
+%  no depth limit.  Precedence is the precedence of the context in which the
+%  term will be printed; if you wish to write a term with operators, then you
+%  should parenthesize the output if Precedence is smaller than the precedence
+%  of that operator.
+%
+%  In printing subterms, you should call print_term/3, described above.  Note
+%  that DepthLimit argument passed to portray_term/3 is the depth limit for
+%  the subterms of Term, so you should usually pass this value to print_term/3
+%  for printing subterms without decrementing it.
+%
+%  The following code should serve as an example.
+
+:- dynamic portray_term/3.			% NU Prolog doesn't support
+						% :- multifile decls.
+
+%  Print "strings" (lists of character codes) as double-quoted strings.
+portray_term([N|Ns], _, _) :-
+	integer(N),				% for efficiency
+	printable_char_list([N|Ns]),
+	!,
+	format("""~s""", [[N|Ns]]).
+
+%  Print maps as MAP{ key->value , key->value , ... }
+portray_term(two(K,V,S1,S2), Depth, _) :-
+	!,
+	(   Depth =:= -1 ->
+		write('MAP{...}')
+	;   
+		tree234__tree234_to_assoc_list(two(K,V,S1,S2), Alist),
+		list__map(pair_to_arrow, Alist, Arrowlist),
+		print_length(Length),
+		Depth1 is Depth + 1,
+		print_list(Arrowlist, Depth1, Length, 1200, [],
+			   'MAP{}', 'MAP{', '}', ' , ')
+	).
+portray_term(three(K1,V1,K2,V2,S1,S2,S3), Depth, _) :-
+	!,
+	(   Depth =:= -1 ->
+		write('MAP{...}')
+	;   
+		tree234__tree234_to_assoc_list(three(K1,V1,K2,V2,S1,S2,S3),
+					       Alist),
+		list__map(pair_to_arrow, Alist, Arrowlist),
+		print_length(Length),
+		Depth1 is Depth + 1,
+		print_list(Arrowlist, Depth1, Length, 1200, [],
+			   'MAP{}', 'MAP{', '}', ' , ')
+	).
+portray_term(four(K1,V1,K2,V2,K3,V3,S1,S2,S3,S4), Depth, _) :-
+	!,
+	(   Depth =:= -1 ->
+		write('MAP{...}')
+	;   
+		tree234__tree234_to_assoc_list(
+			four(K1,V1,K2,V2,K3,V3,S1,S2,S3,S4),
+			Alist),
+		list__map(pair_to_arrow, Alist, Arrowlist),
+		print_length(Length),
+		Depth1 is Depth + 1,
+		print_list(Arrowlist, Depth1, Length, 1200, [],
+			   'MAP{}', 'MAP{', '}', ' , ')
+	).
+
+
+printable_char_list(0) :-			% hack to catch unbound tails
+	!,
+	fail.
+printable_char_list([]).
+printable_char_list([N|Ns]) :-
+	integer(N),
+	(   N >= 32, N =< 126 -> true
+	;   N =:= 0'\n -> true
+	;   N =:= 0'\r -> true
+	;   N =:= 0'\t
+	),
+	printable_char_list(Ns).
+	
+
+pair_to_arrow(X-Y, (X->Y)).
+
 
 %-----------------------------------------------------------------------------%
Index: doc/user_guide.texi
===================================================================
RCS file: /home/staff/zs/imp/mercury/doc/user_guide.texi,v
retrieving revision 1.97
diff -u -r1.97 user_guide.texi
--- user_guide.texi	1997/09/23 14:00:16	1.97
+++ user_guide.texi	1997/10/28 06:58:59
@@ -335,9 +335,10 @@
 debugging problems that the compiler could have detected for you.
 
 @menu
-* Using NU-Prolog:: Building and debugging Mercury programs with NU-Prolog
-* Using SICStus::   Building and debugging Mercury programs with SICStus Prolog
-* Prolog hazards::  The hazards of executing Mercury programs using Prolog
+* Using NU-Prolog::      Building and debugging Mercury programs with NU-Prolog
+* Using SICStus::        Building and debugging with SICStus Prolog
+* Controlling Printout:: Controlling printout of large terms during debugging
+* Prolog hazards::       The hazards of executing Mercury programs using Prolog
 @end menu
 
 @node Using NU-Prolog
@@ -427,6 +428,9 @@
 By default the debugger only displays the top levels of terms;
 you can use the @samp{|} command to enter an interactive term browser.
 (Within the term browser, type @samp{h.} for help.)
+See @ref{Controlling Printout} for more information on controlling the
+printout of terms.
+
 Also note that in the debugger, we use a version of @code{error/1}
 which fails rather than aborting after printing the ``Software Error:''
 message.  This makes debugging easier,
@@ -456,6 +460,174 @@
 which instructs @code{msc} to use SICStus Prolog's @samp{compactcode}
 mode, which compiles files to a bytecode format.
 
+ at node Controlling Printout
+ at section Controlling Printout of large terms
+
+You can control the printout of terms shown as the results of queries,
+and as shown by the debugger, to a great extent.  At the coarsest level,
+you can limit the depth to which terms are shown, and the number of
+arguments of terms (and the number of elements of lists) to be shown.
+Further, you can ``hide'' some of the arguments of a term, based upon the
+functor of that term.  And finally, you can write your own code to print
+out certain kinds of terms in a more convenient manner.
+
+ at menu
+* Limiting Size::  Limiting Print Depth and Length
+* Hiding Terms::   Hiding terms and subterms based upon term functor
+* Customization::  Customizing printout by writing your own printout code
+ at end menu
+
+ at node Limiting Size
+ at subsection Limiting Print Depth and Length
+
+You can limit the depth to which terms will be printed.  When a term to
+be printed appears at the depth limit, only its name and arity will be
+shown, enclosed in angle brackets, for example @samp{<foo/3>}.
+
+You can also control the number of arguments of terms, and the lengths
+of lists, to be shown.  When this length limit is reached, an elipsis
+mark (@samp{...}) will be printed.
+
+The following predicates control the print depth and length limits.
+
+ at example
+set_print_depth(@var{Depth})
+ at end example
+Sets the print depth limit to @var{Depth}.  If @var{Depth} is less than 0, the
+print depth is unlimited.  The default print depth limit is 2.
+
+ at example
+print_depth(@var{Depth})
+ at end example
+Unifies @var{Depth} with the current print depth limit.
+
+ at example
+set_print_length(@var{Length})
+ at end example
+Sets the print length limit to @var{Length}.  If @var{Length} is less
+than 0, the print length is unlimited.
+
+ at example
+print_length(@var{Length})
+ at end example
+Unifies @var{Length} with the current print length limit.
+
+
+ at node Hiding Terms
+ at subsection Hiding Terms
+
+Sometimes you will have terms which appear frequently during debugging
+and which tend to fill up the screen with lots of uninteresting text,
+even with depth limits in force.  In such cases, you may want to hide
+terms based upon their functor, regardless of their print depth.  You
+may also occasionally want to hide just a few of the arguments of a
+term.  These predicates allow you to do that.
+
+ at example
+hide(@var{Spec})
+hide(@var{Spec, Args})
+ at end example
+Allows you to elide certain arguments of certain terms, or whole terms,
+based on the functor.  @var{Spec} specifies a functor; it must be either
+a @var{Name}@code{/}@var{Arity} term, or just a functor name (in which
+case all arities are specified).  @var{Args} specifies which arguments
+of such terms should be hidden.  If given, @var{Args} must be a list of
+integers or a single integer specifying which arguments to hide.  Hidden
+arghuments will be printed as just a hash (@samp{#}) character.
+Alternatively, @var{Args} may be the atom @code{term}, in which case
+only the name and arity of the term are written within angle brackets,
+(e.g., @samp{<foo/3>}).  If Args is not specified, it defaults to
+ at code{term}.
+
+ at example
+show
+Show(@var{Spec})
+show(@var{Spec, Args})
+ at end example
+Undos the effect of hiding some argumments of terms or whole terms.
+ at var{Spec} specifies a functor; it must be either a
+ at var{Name}@code{/}@var{Arity} term, or just a functor name (in which
+case all arities are specified).  @var{Args} specifies which arguments
+to stop hiding; it must be a list of argument numbers or a single
+argument number.  Alternatively, it may be the atom @code{term},
+indicating that no arguments should be hidden (the whole term should be
+shown).  @var{Args} defaults to @code{term}.  The predicate
+ at code{show/0} reenables showing all arguments of all terms.
+
+Note that these predicates will not affect the depth limit of printing.
+Even if you ask to show a term in full, if it appears at the depth limit of
+the term being printed, its arguments will not be shown.  If it appears
+below the depth limit, you won't see it at all.
+
+ at example
+hidden(@var{Spec, Args})
+ at end example
+ at var{Spec} is a @var{Name}@code{/}@var{Arity} term, and @var{Args} is a
+list of the argument numbers of terms with that @var{Name} and
+ at var{Arity} which are hidden, or the atom @code{term} if the entire term
+is hidden.  @var{Arity} may be the atom @code{all}, indicating that the
+specified arguments are hidden for all terms with that @var{Name} and
+any arity.  This predicate may be used to backtrack through all the
+predicates which are hidden or have any arguments hidden.
+
+
+ at node Customization
+ at subsection Customization of Printout
+
+Occasionally you will have a term that is important to understand during
+debugging but is shown in an inconvenient manner or may be difficult to
+understand as printed.  Traditionally in Prolog, you can define clauses
+for the predicate @code{portray/1} to specify special code to print such
+a term.  Unfortunately, this won't work very well with the other
+features of Mercury's printout system.  For this you will need to define
+clauses for the predicate @code{portray_term/3}:
+
+ at example
+portray_term(@var{Term, DepthLimit, Precedence})
+ at end example
+Like the standard Prolog @code{portray/1} hook predicate, this code
+should either fail without printing anything, or succeed after printing
+out Term as the user would like to see it printed.  @var{DepthLimit}, if
+greater than or equal to -1, is the number of levels of the subterms of
+ at var{Term} that should be printed without being elided.  If it is -1,
+then @var{Term} should probably not be printed; only a marker indicating
+what sort of term it is.  The standard @code{print_term/3} code will
+print the name and arity of the functor enclosed in angle brackets
+(e.g., <foo/3>) if @code{portray_term/3} fails at this depth level.  Be
+careful, though: if @var{DepthLimit} is less than -1, then there should
+be no depth limit.  @var{Precedence} is the precedence of the context in
+which the term will be printed; if you wish to write a term with
+operators, then you should parenthesize the printout if @var{Precedence}
+is smaller than the precedence of that operator.
+
+In printing subterms, you should call @code{print_term/3}, described
+below.  Note that @var{DepthLimit} argument passed to portray_term/3 is
+the depth limit for the @emph{subterms} of @var{Term}, so you should
+usually pass this value to @code{print_term/3} for printing subterms
+without decrementing it.  Note also that @var{Term} will always be bound
+when @code{portray_term/3} is called, so you needn't worry about
+checking for unbound variables.
+
+Mercury already supplies special-purpose printout code to print out
+lists of character codes as double-quoted strings, and to print out maps
+as @var{Key}@code{->}@var{Value} pairs, surrounded by @samp{MAP@{@}}.
+This code appears in the file @file{portray.nl} in the Mercury library,
+and may serve as a useful example of how to write customized printing
+code.
+
+ at example
+print_term(@var{Term, DepthLimit, Precedence})
+ at end example
+Print out @var{Term} to the current output stream, limiting print depth
+to @var{DepthLimit}, and parenthesizing the term if its principle
+functor is an operator with precedence greater than @var{Precedence}.
+ at code{print_term/3} also respects the current set of hidden functors,
+and uses the user-supplied @code{portray_term/3} hook predicate when it
+succeeds.
+
+
+
+
 @node Prolog hazards
 @section Hazards of using Prolog
 
@@ -1472,7 +1644,7 @@
 library which has been compiled with the same setting.
 Rather than setting them individually, you must
 specify them all at once by selecting a particular
-compilation model ("grade").
+compilation model (``grade'').
 
 @table @asis
 @item @code{-s @var{grade}}



More information about the developers mailing list