:- module salut.
:- interface.
:- import_module io, list.

:- typeclass greetable(T) where [
    func the(T) = string,
    func name(T) = string
].

:- type traffic(T)  % <= (greetable(T))
    --->    arriving(T)
    ;       leaving(T)
    ;       nothing(int).  % nothing for N seconds

    % announce(TrafficEvents, !IO)
    %
    % Make grand announcements according to a list of traffic events.
    %
:- pred announce(list(traffic(T))::in, io::di, io::uo) is det <= (greetable(T)).

    % hello(TrafficEvent, !IO)
    %
    % Respond appropriately to a greetable in motion.
    %
:- pred hello(traffic(T), io, io) <= (greetable(T)).
:- mode hello(in, di, uo) is det.

:- implementation.
:- import_module string.

announce(Events, !IO) :- foldl(announcement, Events, !IO).

hello(nothing(_), !IO).
hello(arriving(Who), !IO) :-
    io.format("Hello, %s.\n", [s(name(Who))], !IO).
hello(leaving(Who), !IO) :-
    io.format("See you again, %s.\n", [s(name(Who))], !IO).

:- pred announcement(traffic(T)::in, io::di, io::uo) is det <= (greetable(T)).
announcement(nothing(N), !IO) :-
    sleep(N, !IO).
announcement(arriving(W), !IO) :-
    io.format("Now arriving: %s %s!\n", [s(the(W)), s(name(W))], !IO).
announcement(leaving(W), !IO) :-
    io.format("%s %s has left the party.\n", [s(the(W)), s(name(W))], !IO).

:- pred sleep(int::in, io::di, io::uo) is det.
:- pragma foreign_decl("C", "#include <unistd.h>").
:- pragma foreign_proc("C",
    sleep(N::in, _IO0::di, _IO::uo),
    [will_not_call_mercury, promise_pure],
"
    sleep(N);
").