PrologHub

Hello, !, the second program improved.

2019-08-28
Paul Brown

Okay, so in Prolog when we get user input at the prompt we usually need our user to be wise enough to close their input with a full-stop. Let's demonstrate, in order for this example to work in Tau Prolog you'll need to input a valid atom and terminate in a full-stop (the input will appear to reset but actually append until a full-stop is included). You can try greet/0 with your favorite Prolog implementation, it'll work in most.

% A writeln substitute for the browser
:- use_module(library(js)).
writeln(Text) :-
    prop(alert, Alert),
    apply(Alert, [Text], _).

greet :- 
    read(Name),
    atom(Name),
    atomic_list_concat(['Hello, ', Name, '!'], Msg),
    writeln(Msg).
greet.

But what if our users weren't so wise? Wouldn't it be nice if you could type in your name without a full-stop? We can replicate the above using JavaScript to compare. In this example you can write your name with a capital letter, without escaping it in single quotes first, and without a full-stop: like most people would.

% A writeln substitute for the browser
:- use_module(library(js)).
writeln(Text) :-
    prop(alert, Alert),
    apply(Alert, [Text], _).

prompt(Msg, X) :-
    prop(prompt, Prompt),
    apply(Prompt, [Msg], X).

greet :-
    prompt('What\'s your name?', Name),
    atomic_list_concat(['Hello, ', Name, '!'], Msg),
    writeln(Msg).
greet.

That's great and all, except we're hijacking JavaScript to do it, what we need is a solution that'll work in the terminal. Here's one solution that works in SWI-Prolog:

read_string(String) :-
    current_input(Stream),
    read_string(Stream, '\n', ' \r', Sep, String0),
    (   Sep \== -1
    ->  String = String0
    ;   String0 == ""
    ).

greet :-
    read_string(Name),
    format("Hello, ~w!", Name).

That's not going to appear in a beginner's book anytime soon! Let's break it down.

First we get the current_input(Stream), which by default is your prompt. We then use read_string/5 . It reads from the Stream up to '\n', which is when your user hit return. The argument ' \r' are the characters stripped off the left and right of the string. Sep is -1 if the user terminated the input (for example by Ctrl+D), otherwise we can unify String0 with String and return that.

Try this out, you can skip the full-stop! We're also reading a string, so case is irrelevant. If you like it, there's a more versatile implementation read_line_to_string/2 that can cope with files and other streams, as well as related predicates in the readutil library.


Tags: io