[comp.lang.prolog] A Question of Style

pbr@mcnc.org (Peter Reintjes) (11/16/89)

I have been doing some Xwindows programming in Prolog
(thanks to the UCLA XWIP system) and I have written the
same fragment of code three different ways. I would be
interested in comments about clarity and how people
choose between styles of writing Prolog code.

The problem is highly non-logical. Given that
we are refreshing the screen, either because
we are drawing a new picture or re-drawing
the old one because the screen was newly exposed,
we want to flush out all pending Expose events.

Here is the code which handled the Expose events
and refreshes the screen,

service(xEvent(xExpose,_,_,_,Window,_,_,_,_,_),Connection) :-
	flush_events(Connection,Window),
	refresh.

flush_events(Conn,Win) :-
  xGetEvent(Conn,Win,[],xExpose,xTrue,xFalse,_)
	-> flush_events(Conn,Win)
	;  true.

I toyed with this structure:

flush_events(Conn,Win) :-
        xGetEvent(Conn,Win,[],xExpose,xTrue,xFalse,_),
	!,
	flush_events(Conn,Win)
flush_events(_,_).

But it didn't seem worth two clauses, also, I don't
really need a REAL cut when the soft-cut of an
if-then-else would do just fine.

Then I really got to thinking about what is going on,
You see, if the xGetEvent succeeds, it means it got
an event and there might be more. If it fails, then
we are done. Sooo....

	repeat,
         \+ xGetEvent(Conn,Win,[],xExpose,xTrue,xFalse,_),
	!,

Does the trick. It is easy to put in-line (no predicate overhead)
but my initial response to this was negative because I try to
avoid repeat and cut whenever possible. Actually, this fragment
is almost nothing BUT logically "bad" things (repeat, negation, cut).

The upshot of this is -- Since I am supposed to be teaching people
how to program REAL programs in Prolog. Should I *recommend* the
last structure?  First I'd like to know which example people prefer?
Any novices out there that can give me a gut reaction?
And maybe implementers can tell me which is more efficient and why.

I frequently have the problem of liking a software trick
because it is "cute" or "minimalist" in some way, but then
being highly suspicious of it as the best way to write code
which is to be read by other people (as all good code is).

Prolog has a much better shot at being a readable than
other languages (APL is too cryptic, while C and other
are too bulky - with lots of code that isn't descriptive
of the problem at hand) and in many cases, the minimal
Prolog solution is the most logical and robust, but when
we are doing non-logical things . . . 


What do you think.

ok@mudla.cs.mu.OZ.AU (Richard O'Keefe) (11/16/89)

In article <1471@speedy.mcnc.org>, pbr@mcnc.org (Peter Reintjes) writes:
I have not seen XWIP and do not know what its data structures are
like, so I'll have to make some guesses here.

Reintjes considers three choices:

(if-then-else)

    flush_events(Conn, Win) :-
	(   xGetEvent(Conn, Win, [], xExpose, xTrue, xFalse, _) ->
	    flush_events(Conn, Win)
 	;   true
	).

(cut)

    flush_events(Conn, Win) :-
	xGetEvent(Conn, Win, [], xExpose, xTrue, xFalse, _),
	!,
	flush_events(Conn, Win).
    flush_events(_, _).

(repeat-not)

 	repeat,
	    \+ xGetEvent(Conn, Win, [], xExpose, xTrue, xFalse, _),
 	!,

With respect to the choice between (if-then-else) and (cut) he says
> I don't really need a REAL cut when the soft-cut of an
> if-then-else would do just fine.

But the cut in an if-then-else is just as hard a cut as any other.
It has a smaller _scope_ than a plain cut, but it is quite hard.
NU Prolog has
	(if <test> then <when true> else <when false>)
and in addition to delaying as necessary to make the test sound, this
form uses a soft cut, just like
	(cases <test> -> <when true> ; <when false>)
in those versions of C Prolog that have cases/1.

There is a fourth alternative provided in some Prologs:

(iterate)

	iterate(xGetEvent(Conn, Win, [], xExpose, xTrue, xFalse, _))

You'll find it mentioned in the back of Sterling & Shapiro.  It is
sometimes called repeat/1, though that's usually succeed-N-times.
iterate(Goal) is something like
	(L: Goal -> goto L ; true ).

The really clean way of doing this would be to use Logix or Strand and have
the sequence of X events present as a list of records (if you try to look
at the next event and it hasn't happened yet the current process is
suspended until it materialises).  I've seen a windowing system done in
Logix and was instantly converted:  that approach really does work.

It looks as though XWIP is handling X events in much the same way that
Prolog character input handles characters.  If that is so, the best way
to go is to think of that part of your program which is concerned with
"parsing" the event sequence as a finite state machine, and to write the
code whatever way makes the structure of this FSM clearest.

I don't think the choice between (if-then-else) and (cut) in this case is
any big deal; whatever difference there is will be swamped by the cost of
the call to xGetEvent.  Both of them strike me as far clearer than the
(repeat) version.  The apparent straightforwardness of the (repeat) and
(iterate) versions evaporates as soon as you want to skip more than one
type of event.

I suggest that you give serious consideration to developing an FSM notation
which lets you make it really really clear how you are parsing the event
sequence and translate that to Prolog with a preprocessor.  There's a fair
bit in the general computing literature about specifying user interfaces
with this or that generalisation of FSMs.

pds@quintus.UUCP (Peter Schachte) (11/18/89)

In article <2727@munnari.oz.au> ok@mudla.cs.mu.OZ.AU (Richard O'Keefe) writes:
>The really clean way of doing this would be to use Logix or Strand and have
>the sequence of X events present as a list of records (if you try to look
>at the next event and it hasn't happened yet the current process is
>suspended until it materialises).  I've seen a windowing system done in
>Logix and was instantly converted:  that approach really does work.

I suspect that this isn't quite what Peter wanted here.  In X, when the
server asks you to redraw the contents of a window, you need to clear
out the queue of events of any more requests to redraw that window, or
you'll redraw multiple times.  If that is the problem to be solved here,
the approach you suggest wouldn't work, since you only want to look at
the events that are already queued.  You certainly don't want to suspend
when you hit the end of the queue, you just want to return.

I have no idea how to do this really cleanly, or if that's possible at
all, but I kind of prefer the if-then-else approach to the other two in
Peter's original posting.
-- 
-Peter Schachte
pds@quintus.uucp
...!sun!quintus!pds