Slices in Prolog

% Slices are an important data type in Prolog, giving
% a more powerful interface to sequences than lists.

% Unlike lists, slices are typed only by the
% elements they contain (not the number of elements).
% An uninitialized slice equals to an empty list and has
% length 0.
empty_slice(S) :-
    S = [],
    length(S, 0).

main :-
    empty_slice(S),
    writeln('uninit:'),
    write(S), write(' '), write(S == []), write(' '), writeln(length(S, 0)),

    % To create an empty slice with non-zero length, use
    % the builtin `length/2`. Here we make a slice of
    % atoms of length 3 (initially uninstantiated).
    length(S1, 3),
    writeln('emp:'),
    write(S1), write(' len: '), length(S1, Len), write(Len),

    % We can set and get just like with lists.
    S2 = [a, b, c],
    writeln('set:'), writeln(S2),
    nth0(2, S2, Element),
    write('get: '), writeln(Element),

    % `length/2` returns the length of the slice as expected.
    length(S2, L),
    write('len: '), writeln(L),

    % In addition to these basic operations, slices
    % support several more that make them richer than
    % lists. One is the append/3 predicate, which
    % returns a slice containing one or more new values.
    append(S2, [d], S3),
    append(S3, [e, f], S4),
    write('apd: '), writeln(S4),

    % Slices can also be copied. Here we create a new
    % slice C of the same length as S4 and copy into C from S4.
    length(C, L),
    append(C, [], S4),
    write('cpy: '), writeln(C),

    % Slices support a "slice" operator with the syntax
    % slice(List, Start, End, Slice).
    slice(S4, 2, 5, L1),
    write('sl1: '), writeln(L1),

    % This slices up to (but excluding) the 5th element.
    slice(S4, 0, 5, L2),
    write('sl2: '), writeln(L2),

    % And this slices up from (and including) the 3rd element.
    slice(S4, 2, L, L3),
    write('sl3: '), writeln(L3),

    % We can declare and initialize a variable for slice
    % in a single line as well.
    T = [g, h, i],
    write('dcl: '), writeln(T),

    % Prolog has built-in predicates for list operations.
    T2 = [g, h, i],
    (T == T2 ->
        writeln('T == T2')
    ;
        writeln('T \== T2')
    ),

    % Slices can be composed into multi-dimensional data
    % structures. The length of the inner slices can
    % vary, unlike with multi-dimensional arrays.
    TwoD = [
        [0],
        [1, 2],
        [2, 3, 4]
    ],
    write('2d: '), writeln(TwoD).

% Helper predicate to slice a list
slice(List, Start, End, Slice) :-
    length(List, Len),
    (End > Len -> EndIndex = Len ; EndIndex = End),
    sublist(List, Start, EndIndex, Slice).

sublist(List, Start, End, Slice) :-
    length(Before, Start),
    length(Slice, Length),
    Length is End - Start,
    append(Before, Rest, List),
    append(Slice, _, Rest).

Note that while slices are different types than lists, they are rendered similarly by write/1.

To run the program, save it as slices.pl and use a Prolog interpreter:

$ swipl -q -f slices.pl -t main
uninit:
[] true true
emp:
[_G2058,_G2064,_G2070] len: 3
set:
[a,b,c]
get: c
len: 3
apd: [a,b,c,d,e,f]
cpy: [a,b,c,d,e,f]
sl1: [c,d,e]
sl2: [a,b,c,d,e]
sl3: [c,d,e,f]
dcl: [g,h,i]
T == T2
2d: [[0],[1,2],[2,3,4]]

In Prolog, we use lists to represent slices, as Prolog doesn’t have a built-in slice type. The operations on slices are implemented using Prolog’s list manipulation predicates and custom helper predicates. The concept of capacity doesn’t directly apply in Prolog, so it’s omitted in this translation.

Now that we’ve seen lists and slices in Prolog, we’ll look at Prolog’s other key data structures in the following examples.