String Formatting in Prolog

Our example demonstrates various string formatting techniques in Prolog. While Prolog doesn’t have a built-in printf-style formatting system like some other languages, we can achieve similar results using Prolog’s powerful string manipulation predicates.

:- use_module(library(format)).

% Define a point structure
point(X, Y) :- number(X), number(Y).

main :-
    % Create a point
    P = point(1, 2),
    
    % Basic structure printing
    format('struct1: ~w~n', [P]),
    
    % Printing with more detail
    format('struct2: point(x:~w, y:~w)~n', [1, 2]),
    
    % Printing the type
    format('type: ~w~n', [point]),
    
    % Formatting booleans
    format('bool: ~w~n', [true]),
    
    % Formatting integers
    format('int: ~d~n', [123]),
    
    % Binary representation
    format('bin: ~2r~n', [14]),
    
    % Character representation
    format('char: ~c~n', [33]),
    
    % Hexadecimal representation
    format('hex: ~16r~n', [456]),
    
    % Formatting floats
    format('float1: ~f~n', [78.9]),
    format('float2: ~e~n', [123400000.0]),
    format('float3: ~E~n', [123400000.0]),
    
    % String formatting
    format('str1: ~w~n', ["string"]),
    format('str2: ~q~n', ["string"]),
    
    % Hexadecimal representation of a string
    string_codes("hex this", Codes),
    format('str3: ~16r~n', [Codes]),
    
    % Pointer representation (not applicable in Prolog, using object identity instead)
    format('identity: ~w~n', [P]),
    
    % Width formatting for integers
    format('width1: |~`_t~6d|~`_t~6d|~n', [12, 345]),
    
    % Width formatting for floats
    format('width2: |~`_t~6.2f|~`_t~6.2f|~n', [1.2, 3.45]),
    
    % Left-justified width formatting for floats
    format('width3: |~`_t~6.2f|~`_t~6.2f|~n', [1.2, 3.45]),
    
    % Width formatting for strings
    format('width4: |~`_t~6s|~`_t~6s|~n', ["foo", "b"]),
    
    % Left-justified width formatting for strings
    format('width5: |~6s|~6s|~n', ["foo", "b"]),
    
    % Equivalent to Sprintf
    format(string(S), 'sprintf: a ~w', ["string"]),
    format('~w~n', [S]),
    
    % Writing to a different stream (stderr in this case)
    format(user_error, 'io: an ~w~n', ["error"]).

To run this program, save it as string_formatting.pl and use your Prolog interpreter. For example, with SWI-Prolog:

$ swipl -s string_formatting.pl -g main -t halt
struct1: point(1,2)
struct2: point(x:1, y:2)
type: point
bool: true
int: 123
bin: 1110
char: !
hex: 1C8
float1: 78.900000
float2: 1.234000e+08
float3: 1.234000E+08
str1: string
str2: "string"
str3: 6865782074686973
identity: point(1,2)
width1: |____12|___345|
width2: |__1.20|__3.45|
width3: |__1.20|__3.45|
width4: |___foo|_____b|
width5: |foo   |b     |
sprintf: a string
io: an error

Note that Prolog’s formatting capabilities differ from those in some other languages. We’ve used the format/2 and format/3 predicates from the format library to achieve similar results. Some concepts, like pointers, don’t directly apply to Prolog, so we’ve adapted them as best as possible.

The width formatting in Prolog works differently. We’ve used the ~_tformat specifier for right-justified padding with underscores. For left-justified formatting of strings, we simply use the~Ns` format specifier where N is the width.

Prolog doesn’t have a direct equivalent to Printf or Sprintf, but we can use format/3 with a string as the first argument to capture the formatted output in a variable.

Lastly, to write to a different stream (like stderr), we can specify the stream as the first argument to format/3. In this case, we’ve used user_error which is typically linked to stderr.